Compare commits

...

1900 Commits

Author SHA1 Message Date
Oscar Zhou
b14438fd99 fix(edge): add agent id/name into edge api response [BE-10988] (#12256) 2024-10-08 19:17:09 +13:00
James Carppe
ba96d8a5fb Update bug report template for 2.21.3 (#12309) 2024-10-08 16:24:16 +13:00
Ali
db4b1dd024 fix(app): fix cpu type for decimals [r8s-107] (#12306) 2024-10-08 11:44:22 +13:00
Ali
469a4e94c2 fix(volumes): update the external, unused badges and used by col [r8s-105] (#12302) 2024-10-08 11:41:47 +13:00
Ali
44d6c0885e fix(node): call node usage [r8s-106] (#12304) 2024-10-08 11:39:05 +13:00
andres-portainer
9ce4ac9c9e fix(oauth): change the logging level from Debug to Error BE-4583 (#12305) 2024-10-07 18:21:05 -03:00
James Carppe
b40d22dc74 Update bug report template for 2.22.0 (#12283) 2024-10-03 14:53:37 +13:00
Steven Kang
a257696c25 fix access conditions when the restrict default namespace is enabled (#12280) 2024-10-02 15:55:05 +13:00
andres-portainer
f742937359 fix(endpoints): optimize the search performance BE-11267 (#12262) 2024-10-01 15:13:54 -03:00
Steven Kang
c0db48b29d fix ingress creation for none class (#12273) 2024-10-01 14:43:46 +13:00
Steven Kang
ea228c3d6d refactor(k8s): namespace core logic (#12142)
Co-authored-by: testA113 <aliharriss1995@gmail.com>
Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>
Co-authored-by: James Carppe <85850129+jamescarppe@users.noreply.github.com>
Co-authored-by: Ali <83188384+testA113@users.noreply.github.com>
2024-10-01 14:15:51 +13:00
Ali
da010f3d08 fix(podman): ensure initial env type matches container runtime [r8s-98] (#12259) 2024-09-30 09:16:24 +13:00
Ali
32e94d4e4e feat(podman): support add podman envs in the wizard [r8s-20] (#12056) 2024-09-25 11:55:07 +12:00
Ali
db616bc8a5 fix(wizard): update nodeport placeholder [r8s-62] (#12255) 2024-09-25 11:36:50 +12:00
James Carppe
b8b46ec129 Update bug report template for 2.21.2 (#12251) 2024-09-24 11:42:15 +12:00
LP B
7d0b79a546 fix(app/images): export images to tar (#12223) 2024-09-23 21:55:45 +02:00
LP B
fd26565b14 fix(app/templates): non admins cannot load templates list (#12235) 2024-09-23 17:54:32 +02:00
Nik Wakelin
e0b6f2283a chore(branding): Changes Linode to Akamai Connected Cloud (#12221) 2024-09-23 09:21:02 +12:00
Oscar Zhou
d3d3d50569 fix(version): add specific version for updater image [BE-11153] (#12227) 2024-09-21 14:54:08 +12:00
andres-portainer
cee997e0b3 fix(edgestacks): reorder operations to properly update the endpoint relations BE-11233 (#12239) 2024-09-20 19:10:28 -03:00
LP B
80f53ed6ec fix(api): skip guessing env when there is no env in DB (#12238) 2024-09-20 17:56:41 -03:00
Chaim Lev-Ari
6f84317e7a feat(system): upgrade on swarm [EE-5848] (#11728)
Co-authored-by: Chaim Lev-Ari <chaim.levi-ari@portainer.io>
Co-authored-by: LP B <xAt0mZ@users.noreply.github.com>
2024-09-20 18:00:38 +02:00
LP B
3cb484f06a fix(app/users): password validation hint + missing message on empty teams list (#12231) 2024-09-20 16:33:13 +02:00
LP B
61353cbe8a fix(app/edge): race between redirects when selecting a template (#12230) 2024-09-20 16:00:40 +02:00
Yajith Dayarathna
d647980c3a updating attest params (#12228) 2024-09-20 11:48:32 +12:00
Oscar Zhou
5740abe31b fix(authorization): add registry button disappear for admin [BE-11228] (#12213) 2024-09-20 08:18:51 +12:00
andres-portainer
5fd4f52e35 fix(jwt): fix handling of non-expiring JWT tokens BE-11242 (#12220) 2024-09-17 18:23:33 -03:00
Yajith Dayarathna
dbe7cd16d4 2024-09-CVE (#12189) 2024-09-11 11:08:46 +12:00
Yajith Dayarathna
2b630ca2dd enabling build attestations (#12211) 2024-09-11 10:57:52 +12:00
Oscar Zhou
2ede22646b fix(version): add specific version for updater image [BE-11153] (#12202) 2024-09-11 08:29:23 +12:00
James Carppe
994b6bb471 Update bug report template for 2.21.1 (#12207) 2024-09-10 14:33:32 +12:00
andres-portainer
92f338e0cd fix(users): fix data-race in userCreate() BE-11209 (#12193) 2024-09-05 22:28:04 -03:00
andres-portainer
7a176cf284 fix(teams): fix data-race in teamCreate() BE-11210 (#12195) 2024-09-05 21:36:13 -03:00
Oscar Zhou
80e607ab30 fix(stack): env placeholder as host path [BE-11187] (#12192) 2024-09-06 08:43:12 +12:00
Anthony Lapenna
6cff21477e service: update stop grace period description (#12173) 2024-09-05 08:47:06 +02:00
Yajith Dayarathna
4bb5a7f480 updating ci workflow (#12183) 2024-09-05 09:19:36 +12:00
andres-portainer
9a88511d00 fix(docker): avoid specifying the MAC address of container for Docker API < v1.44 BE-10880 (#12179) 2024-09-03 10:31:24 -03:00
Yajith Dayarathna
48cd614948 CVE 2024 43798 (#12171) 2024-09-03 09:27:24 +12:00
andres-portainer
2fe252d62b fix(jwt): generate JWT IDs BE-11179 (#12175) 2024-09-02 12:06:39 -03:00
LP B
8fae7f8438 feat(app/wizard): info panel telling to add env only once per swarm cluster (#11954) 2024-09-02 14:22:07 +02:00
andres-portainer
e4e55157e8 fix(bouncer): add support for JWT revocation BE-11179 (#12164) 2024-08-30 20:24:05 -03:00
Yajith Dayarathna
a5e246cc16 testing go directive change (#12124) 2024-08-30 08:27:42 +02:00
andres-portainer
d28dc59584 fix(git): optimize listFiles() BE-11184 (#12160) 2024-08-29 19:01:51 -03:00
andres-portainer
5353570721 task(code): remove unnecessary uses of govalidator BE-11181 (#12156) 2024-08-28 19:37:20 -03:00
andres-portainer
eb3e367ba8 fix(edgestacks): change the level of a logged line EE-6874 (#11396) 2024-08-28 18:16:34 -03:00
Chaim Lev-Ari
3c1441d462 refactor(users): migrate list view to react [EE-2202] (#11914) 2024-08-28 17:04:32 -03:00
Chaim Lev-Ari
33ce841040 refactor(docker/events): migrate list view to react [EE-2228] (#11581) 2024-08-28 16:41:15 -03:00
Chaim Lev-Ari
9797201c2a feat(docker): label gpu as nvidia only [EE-6999] (#11729) 2024-08-28 16:38:27 -03:00
Chaim Lev-Ari
6e14ac583b fix(access-control): fix dt column header typo [EE-7113] (#11853) 2024-08-28 16:37:12 -03:00
Anthony Lapenna
0b37b677c1 refactor: fix linting issues across the codebase (#12152) 2024-08-28 15:03:15 +02:00
Oscar Zhou
f59dd34154 fix(swarm/service): list task when filtering service [BE-11029] (#12146) 2024-08-28 18:28:38 +12:00
James Carppe
e8ec648886 Update bug report template for 2.21.0 (#12145) 2024-08-27 16:42:49 +12:00
Ali
10767a06df fix(invalidate): keep invalidate default behaviour [BE-11064] (#12080) 2024-08-27 09:48:50 +12:00
James Carppe
59b3375b59 Update bug report template for 2.21.0-rc2 (#12128) 2024-08-23 10:55:43 +12:00
andres-portainer
4408fd0cd3 chore(polling): simplify the polling logic BE-4585 (#12121) 2024-08-22 10:54:34 -03:00
Yajith Dayarathna
975a9517b9 undo change to go directive 2024-08-22 16:21:13 +12:00
Yajith Dayarathna
89c92b7834 updating go directive 2024-08-22 16:17:28 +12:00
Anthony Lapenna
747cea8084 security: bump dependencies to address CVEs (#12119) 2024-08-21 20:08:25 +12:00
Ali
f016b31388 fix(docker-desktop): support auth cookies [BE-11134] (#12108) 2024-08-21 18:21:51 +12:00
Oscar Zhou
8cd53a4b7a fix(registry): non admin can see add registry button [BE-10834] (#12112) 2024-08-21 11:00:00 +12:00
LP B
a39abe61c2 fix(api/edge_stacks): ensure edge stacks related endpoints list generation returns unique elements (#12101) 2024-08-20 10:20:03 +02:00
James Carppe
054898f821 Update bug report template for 2.21.0-rc1 (#12104) 2024-08-15 19:27:24 +12:00
Oscar Zhou
13d9b12a2e fix(group): create group twice when associating devices [EE-7418] (#12092) 2024-08-12 17:09:49 +12:00
LP B
aaec856282 fix(app/registries): enforce user accesses on registries (#12087) 2024-08-10 11:53:16 +02:00
andres-portainer
009eec9475 fix(compose): avoid the need to pass the file to remove the stack BE-11057 (#12065)
Co-authored-by: andres-portainer <andres-portainer@users.noreply.github.com>
Co-authored-by: Yajith Dayarathna <yajith.dayarathna@portainer.io>
2024-08-09 10:22:31 -03:00
Yajith Dayarathna
8d14535fd5 updating github workflow 2024-08-09 14:58:20 +12:00
Oscar Zhou
cc7f14951c fix(stack/remote): pass forceRecreate setting [EE-7374] (#12051) 2024-08-06 09:02:21 +12:00
Yajith Dayarathna
b67ff87f35 Installing docker-compose during test-server step (#12075) 2024-08-05 11:28:47 +12:00
andres-portainer
f55ef6e691 fix(pendingactions): remove excessive logging BE-11094 (#12071) 2024-08-02 16:35:14 -03:00
andres-portainer
560a1a00ca fix(scheduler): remove jobs that won't be used anymore BE-11045 (#12058) 2024-08-01 10:59:29 -03:00
andres-portainer
3b5ce1b053 fix(scheduler): remove unnecessary goroutines BE-11044 (#12059) 2024-08-01 10:58:53 -03:00
andres-portainer
03e8d05f18 fix(scheduler): fix a data race in a unit test BE-11084 (#12057) 2024-08-01 10:58:08 -03:00
Oscar Zhou
bedb7fb255 fix(swarm): auto multi-select volume with same name [EE-7240] (#11955) 2024-07-31 12:12:26 +12:00
Oscar Zhou
4d586f7a85 fix(docker): missing browse volume option [EE-7179] (#11901) 2024-07-30 08:53:17 +12:00
Oscar Zhou
6486a5d971 fix(edgestack): broken parallel setting in create view [EE-7245] (#11945) 2024-07-29 09:42:05 +12:00
andres-portainer
e3364457c4 fix(security): update github.com/containers/image/v5 to fix GO-2024-2842 BE-11055 (#12046) 2024-07-23 18:56:17 -03:00
andres-portainer
66119a8b57 fix(snapshots): remove the attempt to snapshot untrusted environments EE-7407 (#12044) 2024-07-23 18:43:31 -03:00
Ali
6eb9e906af fix(placements) filter out empty items in the required node affinity array [BE-11022] (#12034)
Co-authored-by: testa113 <testa113>
2024-07-23 09:31:08 +12:00
LP B
1900fb695d fix(docker/container): use nodeName to build links to networks used by containers (#12002) 2024-07-17 14:40:05 +02:00
Oscar Zhou
a62aac296b fix(host): show clear host info message [EE-7075] (#12011) 2024-07-12 08:45:50 +12:00
Oscar Zhou
5294aa2810 fix(stack): excessive alias count error [EE-7305] (#11990) 2024-07-11 14:09:25 +12:00
andres-portainer
31bdb948a8 chore(code): use int ranges in loops BE-10990 (#12028) 2024-07-10 19:22:47 -03:00
andres-portainer
468c12c75b chore(bbolt): update to v1.3.10 EE-7298 (#12006) 2024-07-09 15:19:27 -03:00
andres-portainer
220fe28830 fix(snapshots): fix background snapshots on environment creation EE-7273 (#12021) 2024-07-09 15:18:13 -03:00
andres-portainer
7fd1a644a6 chore(loops): remove loop var copy EE-7342 (#12023) 2024-07-09 12:47:34 -03:00
andres-portainer
6e7a42727a chore(kompose): remove the code EE-4917 (#12003) 2024-07-08 17:19:07 -03:00
LP B
ac4b129195 fix(docker/network): send target nodeName when removing a network on swarm (#12001) 2024-07-08 17:31:18 +02:00
Steven Kang
85bc14e470 fix(cve): remediate cves detected in docker scout (#12018) 2024-07-08 10:24:39 +12:00
Yajith Dayarathna
6e791a2cfe (fix)nightly code security scan (#12017) 2024-07-06 10:54:41 +12:00
andres-portainer
340830d121 chore(docker): clean up the code EE-7325 (#11997) 2024-07-05 16:19:46 -03:00
andres-portainer
faca64442f chore(code): use cmp.Or() EE-7333 (#12009) 2024-07-04 19:23:53 -03:00
andres-portainer
854474478c chore(go): update to Go v1.22.5 EE-7297 (#12005) 2024-07-03 22:29:20 -03:00
andres-portainer
4adce14485 fix(errors): improve error handling EE-4430 (#11987) 2024-06-28 17:35:26 -03:00
andres-portainer
dc62604ed8 chore(code): remove unused third-party code EE-7306 (#11988) 2024-06-28 17:33:53 -03:00
andres-portainer
f0d43f941f chore(code): replace interface{} with any EE-6513 (#11986) 2024-06-28 14:59:28 -03:00
andres-portainer
9c4935286f chore(jsoniter): remove leftover code EE-6702 (#11984) 2024-06-28 09:46:49 -03:00
andres-portainer
e1648425ea chore(nomad): remove Nomad code EE-7234 (#11980) 2024-06-28 08:54:44 -03:00
andres-portainer
19fa40286a chore(fdo): remove FDO code EE-7235 (#11981) 2024-06-28 08:42:16 -03:00
andres-portainer
1a3db327c7 fix(kube): improve error handling EE-7196 (#11976) 2024-06-27 10:45:11 -03:00
andres-portainer
1170004097 fix(kube): improve error handling EE-7199 (#11974) 2024-06-27 10:43:44 -03:00
Ali
d2b0eacbf5 chore(deps): upgrade typescript to 5.5 [EE-7294] (#11970)
Co-authored-by: testa113 <testa113>
2024-06-27 13:54:10 +12:00
andres-portainer
ca9f85a1ff fix(snapshots): enable the background snapshotter EE-7273 (#11973) 2024-06-26 18:27:39 -03:00
andres-portainer
9ee092aa5e chore(code): reduce the code duplication EE-7278 (#11969) 2024-06-26 18:14:22 -03:00
Oscar Zhou
39bdfa4512 fix(edgestack): gitops auto update issue [EE-7260] (#11965) 2024-06-27 08:48:03 +12:00
cmeng
e828615467 fix(host-info) host info improvement EE-7075 (#11884) 2024-06-26 12:18:22 -03:00
Ali
ba4526985a fix(app): show services explanation in app form [EE-7284] (#11967)
Co-authored-by: testa113 <testa113>
2024-06-26 10:44:20 +12:00
Chaim Lev-Ari
607feb183e refactor(ui/button): remove duplicate data-cy [EE-7242] (#11934) 2024-06-20 15:39:03 +02:00
LP B
9994ed157a fix(app): properly update the app state when losing connectivity to a remote environment while browsing it (#11942) 2024-06-19 13:44:57 +02:00
andres-portainer
bfa27d9103 chore(code): clean up the code EE-7251 (#11948) 2024-06-18 15:59:12 -03:00
Ali
be9d3285e1 fix(custom-templates): add stack validation, remove custom template validation [EE-7102] (#11938)
Co-authored-by: testa113 <testa113>
2024-06-17 09:24:54 +12:00
Chaim Lev-Ari
0f5988af49 fix(edge/stacks): load template [EE-7109] (#11848) 2024-06-16 07:54:00 +03:00
Chaim Lev-Ari
a28bd349ae fix(edge/update): show environment count when more than 100 [EE-6424] (#11917) 2024-06-14 18:37:45 -03:00
Chaim Lev-Ari
51f9977885 fix(endpoints): show toaster on delete [EE-7170] (#11889) 2024-06-13 18:32:17 -03:00
Ali
27865981df fix(namespace): sanitize owner label [EE-7122] (#11935)
Co-authored-by: testa113 <testa113>
2024-06-13 11:06:17 +12:00
James Carppe
ac3f1cd5c3 Add support for specifying the NFS server address in the mount point EE-7019 (#11921) 2024-06-12 11:23:08 -03:00
Dakota Walsh
7549b6cf3f fix(kubernetes): cluster setup screen text on own line EE-7112 (#11905) 2024-06-12 08:43:17 +12:00
Oscar Zhou
dd372ee122 fix(customtemplate): duplicated error handling [EE-7197] (#11913) 2024-06-11 22:11:15 +12:00
LP B
6a8e6734f3 feat(app): limit the docker API version supported by the frontend (#11855) 2024-06-10 20:54:31 +02:00
andres-portainer
4ba16f1b04 chore(errors): remove superfluous error handling EE-7192 (#11909) 2024-06-10 09:57:02 -03:00
andres-portainer
90a19cec5c chore(code): remove unnecessary type conversions EE-7191 (#11908) 2024-06-10 09:32:52 -03:00
Chaim Lev-Ari
8e480c9fab fix(ui): add accessibility labels to access control fieldset (#11439) 2024-06-09 14:34:22 +03:00
Chaim Lev-Ari
b0e3afa0b6 feat(edge/stacks): default refresh rate to 10s [EE-7155] (#11891) 2024-06-09 14:17:21 +03:00
Chaim Lev-Ari
eb6d251a73 feat(edge/jobs): migrate item view to react [EE-2220] (#11887) 2024-06-06 21:07:39 +03:00
Matt Hook
62c2bf86aa fix(db): fix missing portainer.edb in backups when encrypted portainer db is used [EE-6417] (#11885) 2024-06-06 12:36:27 +12:00
Oscar Zhou
4a7f96caf6 fix(stack): unable to delete invalid stack [EE-5753] (#11813) 2024-06-04 11:34:02 +12:00
Chaim Lev-Ari
9c70a43ac3 refactor(edge/groups): migrate view to react [EE-2219] (#11758) 2024-06-02 15:43:37 +03:00
Chaim Lev-Ari
b7cde35c3d fix(ui/datatables): make empty table label consistent [EE-6499] (#11612) 2024-06-02 12:29:20 +03:00
Chaim Lev-Ari
02fbdfec36 feat(edge/jobs): migrate create view to react [EE-2221] (#11867) 2024-06-02 11:10:38 +03:00
Chaim Lev-Ari
94c91035a7 refactor(custom-templates): migrate list view to react [EE-2256] (#11611) 2024-05-30 12:04:28 +03:00
Matt Hook
5c6c66f010 ix(pendingactions): fix deadlock and reduce needless debug logging [EE-7049] (#11869) 2024-05-30 14:55:16 +12:00
Oscar Zhou
0c870bf37b fix(compose): add project directory option to compose command [EE-7093] (#11870) 2024-05-30 08:47:07 +12:00
matias-portainer
9e0e0a12fa fix(waiting-room): add support for bulk deletion in waiting room EE-7136 (#11879) 2024-05-28 17:18:23 -03:00
andres-portainer
c5a1d7e051 fix(tunnels): make the tunnels more robust EE-7042 (#11877) 2024-05-28 16:42:56 -03:00
andres-portainer
aaab2fa9d8 fix(tls): add support for more cipher suites EE-7150 (#11874) 2024-05-28 15:49:31 -03:00
andres-portainer
ef4beef2ea task(endpoints): change the definition of /endpoints/remove EE-7126 (#11873) 2024-05-28 09:05:35 -03:00
Chaim Lev-Ari
1261887c9e fix(stacks): store filter state [EE-5159] (#11637) 2024-05-28 08:14:12 +03:00
cmeng
84fe3cf2a2 fix(stack): remove tailing slash of git url EE-6664 (#11773) 2024-05-28 09:24:29 +12:00
Chaim Lev-Ari
50fd7c6286 feat(docker/containers): limit items on volume selector [EE-7077] (#11845) 2024-05-23 13:15:36 +03:00
cmeng
d7b412eccc fix(container): replace container using correct node name EE-7066 (#11847) 2024-05-23 09:13:49 +12:00
Oscar Zhou
d283c63a33 fix(api/docker): no authorized user can call restricted api [EE-6808] (#11480) 2024-05-22 09:09:06 +12:00
James Carppe
d15e2cdc0c Update bug report template for 2.20.3 (#11846) 2024-05-21 12:50:29 +12:00
Matt Hook
9cef912c44 feat(dashboard): dashboard api [EE-7111] (#11843) 2024-05-21 11:09:29 +12:00
Oscar Zhou
659abe553d fix(edge/stack): edge stack env table pagination and action [EE-6836] (#11837) 2024-05-21 09:40:11 +12:00
Chaim Lev-Ari
014a590704 refactor(docker): migrate dashboard to react [EE-2191] (#11574) 2024-05-20 09:34:51 +03:00
cmeng
2669a44d79 fix(react-query): set react-query networkMode to offlineFirst EE-7081 (#11812) 2024-05-20 15:29:56 +12:00
Matt Hook
db8f9c6f6c fix(console): fix command not found [EE-6982] (#11825) 2024-05-20 14:35:29 +12:00
andres-portainer
2b01136d03 feat(demo): remove demo mode EE-6769 (#11841) 2024-05-17 20:00:01 -03:00
andres-portainer
fbbf550730 fix(endpoints): remove all the endpoints in the same transaction EE-7095 (#11839) 2024-05-17 16:45:06 -03:00
cmeng
3924d0f081 fix(deletion): delete objects batch by batch EE-7084 (#11833) 2024-05-16 14:34:50 +12:00
Matt Hook
00ab9e949a fix(pending-actions): correctly detect unreachable/down cluster [EE-7049] (#11809) 2024-05-16 09:03:10 +12:00
Chaim Lev-Ari
42d9dfba36 fix(docker/volumes): return 409 on volume conflict [EE-6748] (#11691) 2024-05-15 08:27:44 +03:00
Chaim Lev-Ari
a808f83e7d fix(ui): use expand button in sidebar and tables [EE-6844] (#11608) 2024-05-15 08:26:23 +03:00
Matt Hook
413b9c3b04 fix(terminal): don't close terminal on websocket close [EE-6631] (#11824) 2024-05-15 16:17:32 +12:00
Matt Hook
7edce528d6 fix(console): remove deprecated httputil and update console [EE-6468] (#10848) 2024-05-15 10:28:21 +12:00
Chaim Lev-Ari
836df78181 fix(templates): remove console.log [EE-7092] (#11815) 2024-05-14 09:11:05 +03:00
Ali
a80aa2b45c fix(app): ensure placement errors surface per node [EE-7065] (#11820)
Co-authored-by: testa113 <testa113>
2024-05-14 13:39:53 +12:00
Ali
9dd9ffdb3b fix(app): redirect to app after edit [EE-6385] (#11772)
Co-authored-by: testa113 <testa113>
2024-05-14 13:34:28 +12:00
Ali
b6daee2850 fix(app): surface placement rules from form [EE-6553] (#11816) 2024-05-14 13:34:06 +12:00
Ali
1ba4b590f4 fix(app): statefulset pvc summary [EE-6760] (#11802) 2024-05-14 13:33:25 +12:00
Ali
e73b1aa49c fix(docker): log cleanup errors during endpointforceupdate [EE-7055] (#11762) 2024-05-13 15:34:13 +12:00
Ali
6b5a402962 fix(errors): surface react docker errors to front end [EE-7053] (#11726)
Co-authored-by: testa113 <testa113>
2024-05-13 15:34:00 +12:00
Ali
55667a878a fix(gitops): manifest validation warning [EE-6859] (#11664) 2024-05-13 15:09:25 +12:00
Ali
a0ab82b866 fix(LDAP): skip pw validation on edit [EE-616] (#11666)
Co-authored-by: testa113 <testa113>
2024-05-13 15:08:48 +12:00
Matt Hook
6a51b6b41e fix(pending-actions): further refactoring [EE-7011] (#11806) 2024-05-10 11:59:58 +12:00
matias-portainer
b4e829e8c6 fix(waiting-room): add icon in list title EE-6687 (#11092) 2024-05-09 19:24:04 -03:00
Oscar Zhou
06ef12d0ff fix(image): github registry image truncated [EE-7021] (#11769) 2024-05-10 09:01:54 +12:00
Chaim Lev-Ari
cd5f342da0 refactor(edge/stacks): migrate edit view to react [EE-2222] (#11648) 2024-05-09 18:02:20 +03:00
Oscar Zhou
27e309754e fix(api): list docker volume performance [EE-6896] (#11541) 2024-05-09 13:02:56 +12:00
Ali
6ae0a972d4 fix(docker): surface node details docker error [EE-7054] (#11752)
Co-authored-by: testa113 <testa113>
2024-05-09 12:01:13 +12:00
Dakota Walsh
014c491205 fix(sidebar): environment names on hover EE-6854 (#11755) 2024-05-08 17:08:07 -04:00
Dakota Walsh
4ef71f4aca fix(account): enable add access token button EE-7059 (#11745) 2024-05-08 17:07:44 -04:00
Matt Hook
5a5a10821d fix(pendingactions): refactor pending actions [EE-7011] (#11780) 2024-05-09 08:10:10 +12:00
cmeng
9685e260ea fix(docker): keep /docker url prefix for DockerHandler EE-7073 (#11801) 2024-05-08 14:26:53 +12:00
Ali
f8871fcd2a fix(auth logs): fix typo in search keyword [EE-6742] (#11790)
Co-authored-by: testa113 <testa113>
2024-05-08 09:15:56 +12:00
Ali
6d17d8bc64 fix(be-overlay): consistency overlay with variants [EE-6742] (#11774)
Co-authored-by: testa113 <testa113>
2024-05-07 16:16:49 +12:00
Ali
46c6a0700f fix(app): show one tooltip to describe rollback feature [EE-6825] (#11777)
Co-authored-by: testa113 <testa113>
2024-05-07 15:27:22 +12:00
cmeng
5f8fd99fe8 fix(container): specify node name when get a container EE-6981 (#11748) 2024-05-07 11:34:46 +12:00
Chaim Lev-Ari
8a81d95253 refactor(edge/stacks): migrate create view to react [EE-2223] (#11575) 2024-05-06 08:08:03 +03:00
Prabhat Khera
f22aed34b5 fix(pending-action): pending action data format [EE-7064] (#11766) 2024-05-06 15:46:51 +12:00
Steven Kang
e75e6cb7f7 fix: windows container capability [EE-5814] (#11764) 2024-05-03 10:56:34 +12:00
Ali
14a365045d fix(configs): update unused badge logic [EE-6608] (#11500)
Co-authored-by: testa113 <testa113>
2024-05-03 09:13:33 +12:00
Prabhat Khera
9b6779515e fix(kubernetes): namespace yaml [EE-6701] (#11747) 2024-05-03 09:12:37 +12:00
Matt Hook
88ee1b5d19 fix(kube): correctly extract namespace from namespace manifest [EE-6555] (#11676)
Co-authored-by: Prabhat Khera <prabhat.khera@portainer.io>
2024-05-02 14:28:11 +12:00
Matt Hook
a45ec9a7b4 fix(kube): fix text in activity and authentication logs teasers [EE-6742] (#11683)
Co-authored-by: testa113 <testa113>
2024-05-02 14:23:56 +12:00
Ali
51605c6442 fix(app): explain rollback tooltip [EE-6825] (#11698)
Co-authored-by: testa113 <testa113>
2024-05-02 14:10:36 +12:00
Dakota Walsh
2fe213d864 fix(metadata): add mutli endpoint delete api EE-6872 (#11550) 2024-04-30 21:32:20 -04:00
Dakota Walsh
439f13af19 fix(migration): improper version EE-7048 (#11712) 2024-04-30 21:30:40 -04:00
James Carppe
2b5ecd3a57 Add 2.20.2 to bug report template (#11751) 2024-05-01 12:55:14 +12:00
cmeng
a9ead542b3 fix(edge-stack): add completed status EE-6210 (#11632) 2024-04-30 13:44:08 +12:00
Ali
7479302043 fix(jwt): handle kubeconfig with no expiry [EE-7044] (#11710)
Co-authored-by: testa113 <testa113>
2024-04-30 09:22:45 +12:00
Ali
10d20e5963 fix(version): reduce github requests [EE-7017] (#11677) 2024-04-26 08:46:02 +12:00
Ali
5a2e6d0e50 fix(app): avoid 'no label' error when deleting external app [EE-6019] (#11671) 2024-04-26 08:42:10 +12:00
andres-portainer
9068cfd892 chore(code): remove superfluous checks EE-7040 (#11692) 2024-04-25 11:25:23 -03:00
Chaim Lev-Ari
5560a444e5 fix(users): return json from create token [EE-6856] (#11577) 2024-04-25 10:10:42 +03:00
Matt Hook
505a2d5523 fix(jwt): upgrade jwt to remove deprecated jwt.StandardClaims [EE-6469] (#10850) 2024-04-23 17:33:36 +12:00
Ali
2463648161 fix(node): check more node role labels [EE-6968] (#11658)
Co-authored-by: testa113 <testa113>
2024-04-23 16:16:41 +12:00
Ali
48cf27a3b8 fix(migration): run post init migrations for edge after server starts [EE-6905] (#11546)
Co-authored-by: testa113 <testa113>
2024-04-23 16:15:28 +12:00
Matt Hook
39fce3e29b fix(published-ports): fix published port link and into a new component [EE-6592] (#11656) 2024-04-23 13:47:37 +12:00
Matt Hook
4f4c685085 fix(settings): fix crash during settings update when not using oauth [EE-7031] (#11662) 2024-04-23 12:58:28 +12:00
Prabhat Khera
d177a70c54 fix(stack): correct documentation link for stack ENV variables [EE-6902] (#11654) 2024-04-23 08:35:34 +12:00
James Carppe
cf8ec631dd Add 2.19.5 to bug report template (#11652) 2024-04-22 13:44:10 +12:00
Ali
ea61f36e5d fix(app): fix app stuck in loading [EE-7014] (#11651)
Co-authored-by: testa113 <testa113>
2024-04-22 13:11:41 +12:00
Oscar Zhou
ffc66647f8 feat(setting/oauth): add authstyle option [EE-6038] (#11610) 2024-04-22 10:35:19 +12:00
Oscar Zhou
6623475035 fix(stack/git): option to overwrite target path during dir move [EE-6871] (#11628) 2024-04-22 10:34:32 +12:00
cmeng
0dd12a218b fix(docker-client): explicitly set docker client scheme EE-6935 (#11520) 2024-04-22 09:00:45 +12:00
Chaim Lev-Ari
5f89d70fd8 refactor(datatables): remove angular table utilities [EE-4700] (#11634) 2024-04-21 04:47:09 +03:00
Ali
3ccbd40232 fix(stacks): conditionally hide node and namespace stacks [EE-6949] (#11527)
Co-authored-by: testa113 <testa113>
2024-04-19 17:33:22 +12:00
Prabhat Khera
7e9dd01265 fix(swagger): swagger docs for http status code 409 [EE-5767] (#11535) 2024-04-19 15:19:13 +12:00
Matt Hook
0fb3555a70 chore(kubectl): update kubectl to latest point release [EE-7018] (#11620) 2024-04-19 11:46:44 +12:00
andres-portainer
73ce754316 fix(workflows): upgrade Go to v1.21.9 EE-6939 (#11641) 2024-04-18 19:03:13 -03:00
Prabhat Khera
d304f330e8 fix(stack): fix stack env variable link [EE-6902] (#11624) 2024-04-19 07:00:22 +12:00
andres-portainer
7333598dba fix(mingit): upgrade to v2.44.0.1 EE-7023 (#11638) 2024-04-18 15:22:05 -03:00
Ali
bb61e73464 refactor(kube): events datatable react migration [EE-6450] (#11583)
Co-authored-by: testa113 <testa113>
2024-04-18 19:14:09 +12:00
Prabhat Khera
c15789eb73 fix(images): consider stopped containers for unused label [EE-6983] (#11629) 2024-04-18 17:14:39 +12:00
andres-portainer
e7a2b6268e fix(docker): upgrade to v24.0.9 EE-7016 (#11617) 2024-04-17 19:37:57 -03:00
andres-portainer
688fa3aa78 fix(go): upgrade Go to v1.21.9 in the nightly security scan EE-6939 (#11614) 2024-04-17 18:09:53 -03:00
Matt Hook
48bc7d0d92 fix(auth): prevent user enumeration attack [EE-6832] (#11589) 2024-04-17 16:08:27 +12:00
Prabhat Khera
d9df58e93a fix(pending-actions): clean pending actions for deleted environment [EE-6545] (#11598) 2024-04-16 15:09:10 +12:00
Oscar Zhou
37bba18c81 fix(api/endpoint): filter status for async devices [EE-6958] (#11509) 2024-04-16 13:37:04 +12:00
Matt Hook
40498d8ddd chore(docker): bump docker client to 26.0.1 [EE-6941] (#11592) 2024-04-16 08:27:58 +12:00
Prabhat Khera
b265810b95 fix(stacks): update info text for stack environment variables [EE-6902] (#11551) 2024-04-16 08:03:40 +12:00
Prabhat Khera
09837769d7 fix(pending-actions): fix create kubeclient to check endpoint status [EE-6545] (#11584) 2024-04-16 07:40:41 +12:00
Matt Hook
cf1fd17626 chore(api): bump docker and protobuf pkgs [EE-6941] (#11566) 2024-04-15 10:53:15 +12:00
Matt Hook
785f021898 chore(unpacker): use APIVersion as unpacker image tag [EE-6974] (#10955)
Co-authored-by: Prabhat Khera <91852476+prabhat-portainer@users.noreply.github.com>
2024-04-15 10:29:52 +12:00
Prabhat Khera
80cc9f18b5 chore(unpacker): use APIVersion as unpacker image tag [EE-6974] (#11506) 2024-04-15 10:29:24 +12:00
Matt Hook
5e7e91dd6d bump helm version (#11562) 2024-04-15 09:18:04 +12:00
Chaim Lev-Ari
1032b462b4 chore(deps): upgrade react-query to v4 [EE-6638] (#11041) 2024-04-14 17:54:25 +03:00
andres-portainer
104307b2b2 fix(protobuf): upgrade protobuf to v1.33 EE-6945 (#11570) 2024-04-12 17:52:35 -03:00
andres-portainer
f8c66a31d9 fix(go): upgrade Go to v1.21.9 EE-6939 (#11554) 2024-04-12 17:08:07 -03:00
Chaim Lev-Ari
2100155ab5 refactor(docker/containers): migrate inspect view to react [EE-2190] (#11005) 2024-04-11 19:07:58 +03:00
Chaim Lev-Ari
de473fc10e refactor(docker): remove EndpointProvider from exec [EE-6462] (#10840) 2024-04-11 19:04:58 +03:00
Chaim Lev-Ari
76e49ed9a8 refactor(kube/apps): migrate table to react [EE-4685] (#11028) 2024-04-11 10:11:17 +03:00
Chaim Lev-Ari
e9ebef15a0 refactor(rbac): migrate access table to react [EE-4710] (#10823) 2024-04-11 09:49:38 +03:00
Chaim Lev-Ari
6ff4fd3db2 refactor(templates): migrate list view to react [EE-2296] (#10999) 2024-04-11 09:29:30 +03:00
Ali
d38085a560 chore(data-cy): require data-cy attributes [EE-6880] (#11453) 2024-04-11 12:11:38 +12:00
Chaim Lev-Ari
3cad13388c refactor(ui): use external/system badge where applicable [EE-6952] (#11475) 2024-04-10 08:56:02 +03:00
Matt Hook
0b62456236 fix(backups): improved archive encryption [EE-6764] (#11489) 2024-04-10 10:45:49 +12:00
Chaim Lev-Ari
c22d280491 refactor(activity-logs): migrate activity logs table to react [EE-4714] (#10891) 2024-04-09 08:53:23 +03:00
Chaim Lev-Ari
960d18998f refactor(registries): migrate gitlab projects table to react [EE-4709] (#10792) 2024-04-09 08:52:44 +03:00
Chaim Lev-Ari
3f3db75d85 refactor(account): migrate access tokens table to react [EE-4701] (#10669) 2024-04-09 08:17:43 +03:00
Chaim Lev-Ari
48aab77058 refactor(rbac): migrate roles table to react [EE-4711] (#10772) 2024-04-09 08:11:29 +03:00
Chaim Lev-Ari
7e53d01d0f refactor(activity-logs): migrate auth logs table to react [EE-4715] (#10890) 2024-04-09 08:10:25 +03:00
Chaim Lev-Ari
bd271ec5a1 refactor(registries): migrate tags table to react [EE-6452] (#10990) 2024-04-09 08:08:14 +03:00
Matt Hook
8913e75484 fix(services): speed up service count on the kubernetes dashboard [EE-6967] (#11526) 2024-04-09 15:50:48 +12:00
Chaim Lev-Ari
c95ffa9e2d refactor(rbac): migrate access viewer table to react [EE-6447] (#11498) 2024-04-08 17:25:38 +03:00
Chaim Lev-Ari
ddb89f71b4 refactor(settings/auth): migrate ldap tables to react [EE-4712] (#10822) 2024-04-08 17:24:45 +03:00
Chaim Lev-Ari
45be6c2b45 refactor(tags): migrate tags to react [EE-4707] (#10771) 2024-04-08 17:23:49 +03:00
Chaim Lev-Ari
a00cb951bc refactor(kube/registries): migrate access table to react [EE-4706] (#10688) 2024-04-08 17:23:12 +03:00
Chaim Lev-Ari
f584bf3830 refactor(registries): migrate list view to react [EE-4704] (#10687) 2024-04-08 17:22:43 +03:00
Chaim Lev-Ari
9600eb6fa1 refactor(tables): use add and delete buttons [EE-6297] (#10668)
Co-authored-by: Chaim Lev-Ari <chaim.levi-ari@portaienr.io>
2024-04-08 17:21:41 +03:00
Chaim Lev-Ari
d88ef03ddb refactor(edge/jobs): migrate results table to react [EE-4679] (#10663) 2024-04-08 13:18:59 +03:00
Matt Hook
dc9d7ae3f1 fix(apikey): don't authenticate api key for external auth [EE-6932] (#11460) 2024-04-08 11:03:52 +12:00
James Carppe
a3c7eb0ce0 Update bug report template for 2.20.1 (#11505) 2024-04-05 14:56:19 +13:00
Chaim Lev-Ari
d1ba484be1 refactor(env/groups): migrate list view to react [EE-4703] (#10671) 2024-04-04 18:54:57 +03:00
Chaim Lev-Ari
521eb5f114 refactor(edge): use native progress tag for deployment counter [EE-6075] (#10936) 2024-04-04 18:12:27 +03:00
Chaim Lev-Ari
66770bebd4 refactor(edge/jobs): migrate view to react [EE-2236] (#10661) 2024-04-04 16:25:32 +03:00
Matt Hook
86c4b3059e fix(kube): use https when port is 443 in various tables [EE-6592] (#11443) 2024-04-04 14:36:38 +13:00
Ali
e3a8853212 fix(app): port namespace limit refresh from EE to CE [EE-6835] (#11483)
Co-authored-by: testa113 <testa113>
2024-04-04 08:19:04 +13:00
Ali
194b6e491d fix(namespace): wait for system ns setting to load before selecting existing ns [EE-6917] (#11481)
Co-authored-by: testa113 <testa113>
2024-04-04 08:18:13 +13:00
Chaim Lev-Ari
a439695248 refactor(users): migrate users table to react [EE-4708] (#10759) 2024-04-03 17:38:32 +03:00
Chaim Lev-Ari
86f1b8df6e refactor(kube/volumes): migrate storage table to react [EE-4697] (#11030) 2024-04-02 23:27:20 +03:00
Chaim Lev-Ari
a5faddc56c refactor(kube/cluster): migrate node apps table to react [EE-4691] (#11016) 2024-04-02 23:12:34 +03:00
Chaim Lev-Ari
9c68c6c9f3 refactor(kube/namespaces): migrate item apps table to react [EE-4693] (#11012) 2024-04-02 22:55:34 +03:00
Chaim Lev-Ari
d99486ee72 refactor(kube/namespaces): remove unused ingresses table [EE-6448] (#11029) 2024-04-02 22:41:45 +03:00
Chaim Lev-Ari
946166319f refactor(kube/apps): migrate integrated apps table to react [EE-4690] (#11025) 2024-04-02 22:37:47 +03:00
Chaim Lev-Ari
26bb028ace refactor(kube/namespaces): migrate table to react [EE-4694] (#10988) 2024-04-02 22:26:22 +03:00
Chaim Lev-Ari
da615afc92 refactor(kube/volumes): migrate to react [EE-4695] (#10987) 2024-04-02 22:10:22 +03:00
LP B
2b53bebcb3 fix(app): replace fields removed by Docker 25 and 26 (#11468)
* fix(app/volume): make optional Container and ContainerConfig fields removed in docker 26

* fix(app/image): use image.Size instead of image.VirtualSize removed in Docker 25
2024-03-29 13:57:14 +01:00
Chaim Lev-Ari
d336a14e50 feat(docker/services): show port ranges [EE-4012] (#10657) 2024-03-27 09:56:00 +02:00
cmeng
4ca6292805 fix(edge-stack): avoid reference of undefined EE-6914 (#11463) 2024-03-27 16:08:08 +13:00
andres-portainer
44ef5bb12a fix(kubernetes): avoid a deadlock EE-6901 (#11442) 2024-03-25 14:19:38 -03:00
andres-portainer
bf600f8b11 fix(kubernetes): sync with EE EE-6906 (#11448) 2024-03-22 16:14:00 -03:00
Prabhat Khera
d6d7afddbc chore(version): version bump to 2.22.0 [EE-6897] (#11438) 2024-03-22 14:37:27 +13:00
James Carppe
61642b8df6 Added 2.20.0 to bug report version option dropdown (#11430) 2024-03-19 16:34:03 +13:00
Matt Hook
07de1b2c06 fix(doclinks): fix help link paths [EE-6861] (#11418) 2024-03-19 11:46:41 +13:00
andres-portainer
bd3440bf3c fix(tests): fix data races EE-6867 (#11387) 2024-03-18 10:56:22 -03:00
Matt Hook
573f003226 fix(docs): fix all remaining webhook app links [EE-6861] (#11393) 2024-03-18 16:28:58 +13:00
Matt Hook
6e169662c2 fix(kube): fix edit application webhook link [EE-6861] (#11391) 2024-03-18 10:21:09 +13:00
cmeng
31658d4028 fix(stack): prepopulate when creating template from stack EE-6853 (#11380) 2024-03-18 09:36:06 +13:00
Oscar Zhou
bb02c69d14 chore(template/git): sync frontend code from ee (#11344) 2024-03-18 08:55:16 +13:00
Matt Hook
73307e164b fix(docs): make all doc links versioned [EE-6861] (#11382) 2024-03-15 16:57:51 +13:00
Matt Hook
9ea5efb6ba fix(stacks): update swagger stacks doc description [EE-6860] (#11384) 2024-03-15 16:47:14 +13:00
cmeng
3cd58cac54 fix(container): make blank string as valid value EE-6852 (#11373) 2024-03-15 09:01:47 +13:00
Prabhat Khera
1303a08f5a fix(auth): make createAccessToken api backward compatible [EE-6818] (#11326)
* fix(auth): make createAccessToken api backward compatible [EE-6818]

* fix(api): api error message [EE-6818]

* fix messages
2024-03-14 09:02:28 +13:00
Ali
3b1d853090 fix(app): only show special message when limits change for existing app resource limit [EE-6837] (#11367)
Co-authored-by: testa113 <testa113>
2024-03-14 08:45:48 +13:00
cmeng
a2a4c85f2d fix(csrf): disable csrf secure cookie EE-6787 (#11300) 2024-03-13 11:22:11 +13:00
LP B
506ee389e3 fix(app): views not loading when quickly navigating in app (#11278) 2024-03-12 15:16:14 +01:00
Chaim Lev-Ari
8635bc9b9c fix(docker): apply private uac to edge admin [EE-6788] (#11285) 2024-03-12 09:59:36 +02:00
cmeng
447f497506 fix(edge-stack): deploy button is disabled EE-6819 (#11355) 2024-03-12 17:19:42 +13:00
Prabhat Khera
71292a60b1 address review commets (#11360) 2024-03-12 11:32:06 +13:00
Ali
51449490fa fix(app): on create don't mention previous values [EE-6837] (#11350)
Co-authored-by: testa113 <testa113>
2024-03-11 16:43:41 +13:00
Prabhat Khera
ae4970f0ed fix(container): autocomplete off for create container form [EE-6761] (#11336)
* autocomplete off doe create container form

* address review commets

* remove auto complete off from forms
2024-03-11 13:39:04 +13:00
Prabhat Khera
e96d5c245d fix(kube): stackname in daemonsets and statefulsets app [EE-6670] (#11352) 2024-03-11 10:04:51 +13:00
Chaim Lev-Ari
f8e3d75797 refactor(tests): wrap tests explicitly with provider [EE-6686] (#11090) 2024-03-10 14:22:01 +02:00
Chaim Lev-Ari
27aaf322b2 fix(kube/config): validate change window start [EE-6830] (#11329) 2024-03-10 09:42:33 +02:00
Matt Hook
b77132dbb1 fix(exec): improve alignment of help icon [EE-6816] (#11339) 2024-03-08 14:03:09 +13:00
Prabhat Khera
c35473f308 fix(kube-stacks): change wordings [EE-6670] (#11334) 2024-03-08 12:15:31 +13:00
Ali
a570073d12 fix(matomo): stop oauth link event [EE-6779] (#11332) 2024-03-08 10:17:29 +13:00
Oscar Zhou
0ad4826fab fix(stack): filter out orphan stacks that have same name as normal stacks [EE-6791] (#11291) 2024-03-08 09:56:10 +13:00
Matt Hook
6db7d31554 fix(exec): fix alignment and text size and alignment [EE-6816] (#11325) 2024-03-07 12:58:05 +13:00
cmeng
21d67a971d fix(menu): edge compute menu not clickable EE-6804 (#11319) 2024-03-07 12:11:58 +13:00
Prabhat Khera
8dfa5efa71 fix(stacks): make stackName kube stack specific field [EE-6670] (#11315)
* fix(stacks): make stackName kube stack specific field [EE-6670]

* fix wordings
2024-03-07 11:31:21 +13:00
Prabhat Khera
529750fa21 fix(UI): axios progress bar loading issue [EE-6781] (#11289) 2024-03-07 11:30:27 +13:00
Ali
96b1d36280 fix(time window): show errors for component [EE-6800] (#11317)
Co-authored-by: testa113 <testa113>
2024-03-07 09:03:22 +13:00
Chaim Lev-Ari
31c5a82749 fix(kube/setup): add a11y labels [EE-6747] (#11307) 2024-03-06 14:57:00 +02:00
Matt Hook
82516620e7 fix(contexthelp): remove extra slash from contexthelp docs link [EE-6780] (#11311) 2024-03-06 16:38:06 +13:00
Matt Hook
d26d5840f1 fix(helm): remove helm insights from the stack datatable [EE-6803] (#11314) 2024-03-06 16:36:58 +13:00
Dakota Walsh
ebd26316bf fix(datatable): title size EE-6774 (#11272) 2024-03-06 08:01:51 +13:00
Chaim Lev-Ari
18dbad232e fix(docker/images): export image [EE-6807] (#11306) 2024-03-05 19:30:48 +02:00
matias-portainer
ebcc98d5c5 fix(edge/templates): get correct default value for selectType env vars EE-6796 (#11294) 2024-03-04 10:35:24 -03:00
Matt Hook
e919da3771 fix(kube): update doc links to match new menu structure [EE-6759] (#11267) 2024-03-01 15:37:21 +13:00
Matt Hook
eda2dd20ee fix(help): add versioned doc links to support LTS/STS docs [EE-6780] (#11281) 2024-03-01 15:36:09 +13:00
cmeng
385fd95779 fix(edge-stacks): take not-found stack as removed EE-6758 (#11248) 2024-03-01 11:50:20 +13:00
cmeng
88185d7f6d fix(container): get old container info correctly EE-6716 (#11216) 2024-03-01 09:14:19 +13:00
cmeng
253cda8cef fix(stack): more space for add button EE-6773 (#11259) 2024-03-01 09:11:41 +13:00
Chaim Lev-Ari
b34afba7cd fix(auth): prevent unauthorized redirect on page load [EE-6777] (#11264) 2024-02-29 09:41:26 +02:00
Chaim Lev-Ari
6c70049ecc feat(kube): add a11y props for smoke tests [EE-6747] (#11263) 2024-02-29 09:26:13 +02:00
Chaim Lev-Ari
42c2a52a6b fix(ci): prevent tests from running twice [EE-6728] (#11197) 2024-02-29 08:11:49 +02:00
Chaim Lev-Ari
19a6a5c608 fix(docker): hide write buttons for non authorized [EE-6775] (#11260) 2024-02-27 12:36:44 +02:00
Prabhat Khera
d8e374fb76 fix(ui): autocomplete on edge custom template and stacks [EE-6761] (#11268) 2024-02-27 20:15:52 +13:00
Matt Hook
84ca6185dc fix(kube): make app autorefresh and show system settings stay [EE-6771] (#11257) 2024-02-27 11:18:44 +13:00
Prabhat Khera
5088634a41 fix(stack): auto complete dropdown in docker stacks [EE-6761] (#11253) 2024-02-26 11:43:15 +13:00
Ali
f6beedf0d5 fix(app): parse nan in validation check [EE-6714] (#11246) 2024-02-26 09:20:54 +13:00
Oscar Zhou
3caf1ddb7d fix(edge/template): validate app template env vars [EE-6743] (#11235) 2024-02-26 09:00:12 +13:00
Chaim Lev-Ari
c622f6da4e fix(docker): prevent non admins from passing security settings [EE-6765] (#11240) 2024-02-25 11:57:22 +02:00
cmeng
9ec7394124 fix(stack): make web editor readonly for git template EE-6706 (#11182) 2024-02-23 13:28:27 +13:00
Matt Hook
af8fde66b0 fix(dependancies): update compose and runc [EE-6744] (#11245) 2024-02-23 11:49:09 +13:00
Prabhat Khera
709315dde5 fix(ui): turn autocomplete off for git deployment [EE-6761] (#11242) 2024-02-23 08:44:03 +13:00
Ali
8856bae5c6 fix(app): NaN validation for autoscaling [EE-6714] (#11237) 2024-02-22 17:36:44 +13:00
Matt Hook
90451bfd47 fix(helm) tighten up helm requests [EE-6722] (#11236) 2024-02-22 11:35:33 +13:00
Ali
0c05539dee fix(input): allow clearing number inputs [EE-6714] (#11186) 2024-02-21 10:43:35 +13:00
Ali
a2a2c6cf3e fix(inputlist): update warning style [EE-6737] (#11221) 2024-02-21 08:29:10 +13:00
Matt Hook
76aa086d79 fix(libhttp): capitalize http error responses for better display [EE-6698] (#11114) 2024-02-21 07:51:46 +13:00
Chaim Lev-Ari
76fdfeaafc fix(ui): check for authorization [EE-6733] (#11208) 2024-02-20 11:06:09 +02:00
Chaim Lev-Ari
5932c78b88 fix(kube/apps): move namespace selector in apps view [EE-6612] (#11024) 2024-02-20 10:14:11 +02:00
Ali
68f5ca249f fix(app): remove insight from helm [EE-6693] (#11213)
Co-authored-by: testa113 <testa113>
2024-02-20 17:25:19 +13:00
Ali
2d87a8d8c3 fix(app): set values in react autoscaling form section [EE-6740] (#11219) 2024-02-20 09:35:27 +13:00
Prabhat Khera
988d4103d4 fix(git): update stack name for git stacks [EE-6670] (#11217) 2024-02-20 09:23:46 +13:00
Chaim Lev-Ari
ce3a1b8ba5 feat(a11y): add labels and roles [EE-6717] (#11181) 2024-02-19 16:37:26 +02:00
Oscar Zhou
6c89d3c0c9 fix(edge/template): custom template git fields not pre-filled [EE-6695] (#11112) 2024-02-19 08:39:05 +13:00
Ali
6b91fbf7f4 fix(app): update app type when changing data access policy [EE-6719] (#11211)
Co-authored-by: testa113 <testa113>
2024-02-19 08:08:22 +13:00
Ali
4f3f5e57b6 fix(app): avoid duplicate env requests [EE-6727] (#11194)
Co-authored-by: testa113 <testa113>
2024-02-16 14:02:05 +13:00
Prabhat Khera
6b3f30e32f fix(ui): update search placeholder [EE-6667] (#11190)
* update search placeholder

* remove box selector description
2024-02-16 12:34:06 +13:00
Matt Hook
bdeedb4018 fix(namespace): fix default namespace quota [EE-6700] (#11185) 2024-02-16 08:20:24 +13:00
Chaim Lev-Ari
50946e087c chore(eslint): add rule to check imports [EE-6730] (#11201) 2024-02-15 17:46:03 +02:00
Chaim Lev-Ari
7b89b04667 fix(auth): export hasAuthorizations [EE-6595] (#11199) 2024-02-15 14:05:52 +02:00
Chaim Lev-Ari
f5f84c5fa4 feat(ui): restrict views by role [EE-6595] (#11010) 2024-02-15 13:29:55 +02:00
Chaim Lev-Ari
437831fa80 feat(edge/stacks): add app templates to deploy types [EE-6632] (#11040) 2024-02-15 09:01:01 +02:00
Chaim Lev-Ari
31f5b42962 feat(auth): add useIsEdgeAdmin hook [EE-6627] (#11057) 2024-02-14 19:50:20 -03:00
Ali
7a6c872948 fix(insight): split insight from input [EE-6693] (#11176)
Co-authored-by: testa113 <testa113>
2024-02-15 10:45:59 +13:00
Chaim Lev-Ari
4bf18b1d65 feat(ui): write tests [EE-6685] (#11081) 2024-02-14 17:25:37 +02:00
Ali
2d25bf4afa fix(configs): correct 'external' display in tables [EE-6649] (#11110)
Co-authored-by: testa113 <testa113>
2024-02-14 11:48:09 +13:00
Ali
56ae19c5ab fix(stacks): add app form stacks input [EE-6693] (#11104) 2024-02-14 09:00:51 +13:00
Matt Hook
cdf9197274 fix(logs): add NOCOLOR option for use when exporting to greylog etc [EE-6696] (#11106) 2024-02-14 07:55:00 +13:00
Ali
901549e8dd fix(kube-owner): owner labels from resources created via manifest [EE-6647] (#11102)
Co-authored-by: testa113 <testa113>
2024-02-12 15:30:49 +13:00
Dakota Walsh
80b1cd19cb fix(restore): add S3 teaser EE-6675 (#11095) 2024-02-12 13:12:45 +13:00
Prabhat Khera
c4942de89b fix(ui): stackname auto fill on create from manifest screen [EE-6688] (#11099)
* fix(ui): stackname auto fill on create from manifest screen [EE-6688]

* address review comment
2024-02-12 10:54:29 +13:00
Ali
80d02f9cd1 fix(auth): isAdmin redirect for wizard [EE-6669] (#11074) 2024-02-12 08:04:39 +13:00
Prabhat Khera
671b22b5d6 fix(ui): scroll issue [EE-6667] (#11084)
* Fix scroll issue

* fix minorissue

* address review comments

* add comment
2024-02-09 15:35:34 +13:00
Steven Kang
43e56bf1c0 fix: pre-release build only after merging (#11097) 2024-02-09 15:26:43 +13:00
Matt Hook
a175619623 fix(docs): fix swagger docs for webhook params [EE-6668] (#11088) 2024-02-09 14:44:14 +13:00
Prabhat Khera
63c11d9310 fix(kube): ingress path duplication issue [EE-6649] (#11086) 2024-02-09 07:49:48 +13:00
Prabhat Khera
4c00b72ae3 fix stack name update issue (#11064) 2024-02-08 13:51:01 +13:00
Matt Hook
f4db09a534 fix(kube-apps): add helm insights, remove namespace insights panel [EE-6671] (#11077) 2024-02-08 11:38:04 +13:00
Prabhat Khera
01cd64037f fix(UI): some minor fixes [EE-6667] (#11061)
* minor tweeks for kubernetes settings

* address review comments
2024-02-06 12:17:38 +13:00
Steven Kang
a93344386c Pre-release as part of the CI (#11066)
* feat: add pre-release
* feat: add extension
* feat: fix typo
2024-02-05 18:24:16 +13:00
Prabhat Khera
a2195caa10 keep labels on edit ingress, configmaps and secrets (#11050) 2024-02-05 16:30:36 +13:00
Ali
9ad78753bc fix(r2a): don't set errors to undefined [EE-6665] (#11059)
Co-authored-by: testa113 <testa113>
2024-02-05 14:24:11 +13:00
Prabhat Khera
517190e28b chore(version): bump to 2.21.0 [EE-6652] (#11047)
* chore(version): bump to 2.21.0 [EE-6652]

* address review comments
2024-02-02 15:17:52 +13:00
Dakota Walsh
5ee6efb145 fix(backup): restore over network share EE-6578 (#11044) 2024-02-01 11:41:32 +13:00
Matt Hook
a618ee78e4 fix(helm): minor helm screen page corrections [EE-6642] (#11045) 2024-02-01 11:34:33 +13:00
Ali
9a1604e775 fix(kubeclient): cache kubeclient by user token [EE-6610] (#11039) 2024-01-31 14:50:41 +13:00
Prabhat Khera
9615e678e6 chore(golang): version upgrade to 1.21.6 [EE-6634] (#11036) 2024-01-31 06:28:53 +13:00
Dakota Walsh
e39c19bcca fix(console): export LANG and LC_ALL for kube app console EE-6593 (#11037) 2024-01-30 15:19:53 +13:00
Matt Hook
16ae4f8681 fix(kube): change pod security policy teaser screen wording [EE-6629] (#11035) 2024-01-30 13:03:54 +13:00
Matt Hook
70deba50ba fix(kube): clear kube cache on login/logout [EE-6620] (#11026) 2024-01-30 10:39:12 +13:00
Dakota Walsh
89359dae8c ix(console): docker console UTF-8 EE-6593 (#11034) 2024-01-30 09:34:10 +13:00
Chaim Lev-Ari
97d227be2a fix(swarm/services): convert webhooks API filters to JSON on list request [EE-6621] (#11031)
Co-authored-by: matias-portainer <matias.spinarolli@portainer.io>
2024-01-29 18:08:25 +02:00
Matt Hook
8a98704111 fix(helm): increase default helm timeouts [EE-6617] 2024-01-29 13:03:11 +13:00
Prabhat Khera
46b2175729 fix(kubernetes): placement rules calculations [EE-6552] (#11013) 2024-01-29 08:00:15 +13:00
Chaim Lev-Ari
1561814fe5 feat(gitops): add autocomplete to ref selector [EE-6245] (#10935) 2024-01-28 15:55:10 +02:00
Chaim Lev-Ari
2826a4ce39 feat(custom-templates): filter templates by edge [EE-6565] (#10979) 2024-01-28 15:54:34 +02:00
Matt Hook
441a8bbbbf fix(helm): add clarifying text and new badge to helm user repo settings table [EE-6609] (#11018) 2024-01-26 12:37:13 +13:00
Ali
2248ce0173 fix(secret): update hide secret tooltip [EE-6568] (#11020)
Co-authored-by: testa113 <testa113>
2024-01-26 11:21:34 +13:00
Dakota Walsh
b640b58371 fix(console): use writeUtf8 instead of environment variables EE-6593 (#11019) 2024-01-26 11:21:00 +13:00
Ali
249b6bc628 fix(secrets): teaser wording updates [EE-6568] (#11017) 2024-01-26 10:28:57 +13:00
Chaim Lev-Ari
4a10c2bb07 feat(version): show git commit and env [EE-6021] (#10748) 2024-01-25 07:41:33 +02:00
Chaim Lev-Ari
52db4cba0e fix(storybook): fix msw stories [EE-6503] (#10985) 2024-01-24 10:06:38 +02:00
Chaim Lev-Ari
079bade139 refactor(kube/app): use structuredClone to copy object [EE-6581] (#11004) 2024-01-24 09:31:33 +02:00
Ali
26e52a0f00 fix(pods): don't add labels to old pod that has none [EE-6587] (#11009) 2024-01-24 14:44:15 +13:00
Ali
3ccc764d40 fix(images): update up to date teaser wording [EE-6537] (#11008)
Co-authored-by: testa113 <testa113>
2024-01-24 14:22:15 +13:00
Dakota Walsh
dd068473d2 fix(console): minor typo in tooltip EE-1976 (#11007) 2024-01-24 12:02:56 +13:00
Dakota Walsh
fe47318e26 fix(terminal): display os specific copy/paste tooltip EE-1976 (#10835) 2024-01-24 09:45:40 +13:00
Dakota Walsh
fc7d9ca2cd fix(secrets): add CE teaser EE-6568 (#11001) 2024-01-24 09:44:50 +13:00
Ali
7bf346bd2d fix(app): no summary for existing pvc on edit [EE-6569] (#11003) 2024-01-24 08:09:59 +13:00
Chaim Lev-Ari
8f0f9d7aaa fix(ui): stub unused modules [EE-6583] (#11006) 2024-01-23 15:22:56 +02:00
Chaim Lev-Ari
69c06bc756 feat(ci): replace jest with vitest [EE-6504] (#10997) 2024-01-23 08:42:52 +02:00
Ali
4a19871fcc fix(app): fix capitalisation typos and match EE codebase [EE-6480] (#11002)
Co-authored-by: testa113 <testa113>
2024-01-23 16:28:00 +13:00
Ali
d5080b6884 fix(r2a): fix layout shifting from errors showing as undefined [EE-6570] (#11000) 2024-01-23 14:16:34 +13:00
Prabhat Khera
f7840e0407 fix(ui): mark resources system correctly [EE-6558] (#10996)
* fix(ui): mark resources system correctly [EE-6558]

* address review comments
2024-01-23 13:49:25 +13:00
andres-portainer
85ae705833 fix(gitops): add singleflight behavior to RedeployWhenChanged calls EE-6377 (#10734) 2024-01-22 19:41:48 -03:00
Ali
77c38306b2 fix(app): get min resource limits [EE-6567] (#10994)
Co-authored-by: testa113 <testa113>
2024-01-23 11:20:24 +13:00
Ali
b81babe682 fix(app): no summary for existing pvc [EE-6569] (#10995) 2024-01-23 11:19:52 +13:00
Ali
4c0049edbe fix(app): allow editing pod services [EE-6480] (#10875)
* fix(app): allow editing pod services [EE-6480]
* address review comment

---------

Co-authored-by: testa113 <testa113>
Co-authored-by: prabhat khera <prabhat.khera@portainer.io>
2024-01-23 10:10:16 +13:00
Oscar Zhou
7cba02226e fix(container): duplicate/edit button causes empty container screen [EE-6566] (#10982) 2024-01-22 10:28:16 +13:00
Ali
a15b7cf39a fix(app): fix namespace validation message for admin [EE-6561] (#10992)
Co-authored-by: testa113 <testa113>
2024-01-22 09:30:11 +13:00
Dakota Walsh
36ab4dfb1a Revert "fix(docs): add APIKey Digest example EE-6199 (#10980)" (#10981)
This reverts commit 7b6e106606.
2024-01-19 14:02:19 +13:00
Dakota Walsh
7b6e106606 fix(docs): add APIKey Digest example EE-6199 (#10980) 2024-01-19 13:16:56 +13:00
Ali
5f040bf788 fix(app): namespace selector fixes [EE-6561] (#10977) 2024-01-19 12:20:44 +13:00
Prabhat Khera
a4739f1701 fix messaging for resourse over commit (#10974) 2024-01-19 12:14:47 +13:00
Ali
59f642ea56 fix(app): persisted volume fixes [EE-6554] (#10975)
Co-authored-by: testa113 <testa113>
2024-01-19 12:14:19 +13:00
Oscar Zhou
fa63432695 fix(stack/template): web editor error shows for changing between same mustache templates [EE-6563] (#10976) 2024-01-19 09:28:09 +13:00
Dakota Walsh
1676fefd97 fix(backup): calculate file size correctly EE-6439 (#10919) 2024-01-18 09:00:01 +13:00
Prabhat Khera
bf66b6c5f3 fix(ui): reset auto-scaling formvalues if needed [EE-6544] (#10969) 2024-01-18 07:59:00 +13:00
Chaim Lev-Ari
115b01cee3 fix(docker): include healthy containers in running [EE-6264] (#10746) 2024-01-17 22:30:12 +07:00
Chaim Lev-Ari
a305fe9e4c feat(stacks): hide redeploy for orphaned stacks [EE-5784] (#10841) 2024-01-17 21:45:08 +07:00
Ali
a58b4f479b fix(app): remove duplicate validation messages [EE-5933] (#10967) 2024-01-17 16:30:30 +13:00
Prabhat Khera
93593e1379 fix(ui): update button disabled when manisfest reverted to the orignial content [EE-6544] (#10968) 2024-01-17 13:56:10 +13:00
Prabhat Khera
51ae2198f6 fix typo in app name (#10965) 2024-01-17 12:15:58 +13:00
Prabhat Khera
ccc97e6f78 fix(ui): app summary [EE-6515] (#10966) 2024-01-17 12:15:22 +13:00
Dakota Walsh
3f28d56bfc fix(teams): show add user notification EE-4899 (#10873) 2024-01-17 12:14:05 +13:00
Matt Hook
3103d498cf fix(docs): fix minor swagger issue and upgrade swag [EE-6548] 2024-01-17 11:27:57 +13:00
Oscar Zhou
47f29002f0 fix(edgestack): repull image not work in git autoupdate [EE-6430] (#10952) 2024-01-17 10:20:59 +13:00
Ali
787c7ec4cc fix(app): remove canUndo function from environment variables [EE-6232] (#10961)
Co-authored-by: testa113 <testa113>
2024-01-17 10:13:53 +13:00
Ali
a8e53a4510 fix(app): hide placement form section [EE-6386] (#10964)
Co-authored-by: testa113 <testa113>
2024-01-17 09:34:29 +13:00
Ali
752be47fcc fix(app): get utilization percentage in payload [EE-6387] (#10962)
Co-authored-by: testa113 <testa113>
2024-01-17 08:33:40 +13:00
Ali
95474b7dc5 fix(app): various persisted folder fixes [EE-6235] (#10963)
Co-authored-by: testa113 <testa113>
2024-01-17 08:31:22 +13:00
Prabhat Khera
7a04d1d4ea fix input cursor moving to the end on edit (#10959) 2024-01-16 16:03:01 +13:00
Prabhat Khera
211fff5ed4 update metrics help text (#10960) 2024-01-16 16:02:26 +13:00
Prabhat Khera
2f2cfad722 fix high contrast theme colors (#10872) 2024-01-16 14:32:24 +13:00
Prabhat Khera
380c16c8dd increase the font size for search panel (#10838) 2024-01-16 14:31:39 +13:00
Prabhat Khera
bbf1900677 Disable update application button on load of edit screen (#10957) 2024-01-16 09:31:44 +13:00
Ali
fcc5736d61 fix(app): use isAdmin check on CE [EE-6231] (#10956)
Co-authored-by: testa113 <testa113>
2024-01-15 15:34:21 +13:00
Ali
ae6333bf7c fix(app): remove duplicate values for multinode cluster [EE-6386] (#10947) 2024-01-15 14:34:54 +13:00
Ali
3a959208a8 fix(app): autoscaling min validation [EE-6387] (#10945) 2024-01-15 14:34:16 +13:00
Prabhat Khera
b3b7cfa77f fix(kube): patching stateful service [EE-6523] (#10948) 2024-01-15 13:30:45 +13:00
Ali
6d71a28584 fix(app): improve resource quota error handling [EE-5933] (#10951) 2024-01-15 13:29:35 +13:00
Dakota Walsh
488fcc7cc5 fix(docs): convert APIKey to string EE-6199 (#10943) 2024-01-15 11:59:39 +13:00
Ali
d750389c67 fix(app): fix exhaused error message [EE-6231] (#10949)
Co-authored-by: testa113 <testa113>
2024-01-15 11:03:38 +13:00
Ali
cb7efd8601 fix(app): fix wording and 2 key validation [EE-6233] (#10944)
Co-authored-by: testa113 <testa113>
2024-01-15 11:01:48 +13:00
Ali
55f66f161e fix(app): fix env var state and validation [EE-6232] (#10941)
Co-authored-by: testa113 <testa113>
2024-01-15 10:56:53 +13:00
Prabhat Khera
067a7d148f update endpoint angular state (#10950) 2024-01-12 16:44:49 +13:00
Prabhat Khera
cf88570c39 update validation for storage classes (#10940) 2024-01-12 09:40:55 +13:00
Prabhat Khera
0e6a175bf6 fix metrics text wordings (#10939) 2024-01-12 08:31:29 +13:00
Oscar Zhou
bb680ef20a fix(git): incorrect git commit url for bitbucket [EE-6446] (#10855) 2024-01-12 08:22:50 +13:00
Oscar Zhou
c6505a6647 fix(docker/container): show exit code in status column if needs [EE-5760] (#10916) 2024-01-12 08:21:38 +13:00
Ali
4e7d1c7088 refactor(app): migrate remaining form sections [EE-6231] (#10938) 2024-01-11 15:13:28 +13:00
Prabhat Khera
0b9cebc685 fix(caching): integrate with axios cache interceptor [EE-6505] (#10922)
* integrate with axios-cache-interceptor
* remove extra headers as not needed
2024-01-11 11:12:53 +13:00
Prabhat Khera
d0b9e3a732 fix(UI): app summary on forvalues update [EE-6515] (#10932)
* app summary on forvalues update

* comment added
2024-01-11 10:14:23 +13:00
Prabhat Khera
b7635feff0 fix rbac message when not enabled (#10933) 2024-01-11 08:28:01 +13:00
Matt Hook
7528cabf5a deep upgrade dependencies, follow-redirects, @babel/traverse, postcss (#10931) 2024-01-10 15:40:05 +13:00
Matt Hook
39eb37d5e5 upgrade circl => v1.3.7 (#10925) 2024-01-10 13:08:26 +13:00
Matt Hook
dbd2e609d7 fix(api-key): add password requirement to generate api key [EE-6140] (#10617) 2024-01-09 11:14:24 +13:00
Chaim Lev-Ari
236e669332 refactor(templates): migrate edit view to react [EE-6412] (#10774) 2024-01-08 14:32:32 +07:00
Chaim Lev-Ari
e142939929 fix(ui): apply controlled input to field [EE-6411] (#10738) 2024-01-08 12:11:31 +07:00
Prabhat Khera
98157350b6 fix(UI): add resourse quota warning is consumed 100% [EE-6508] (#10914)
* add resourse quota warning is consumed 100%

* address review comments
2024-01-08 13:49:57 +13:00
Prabhat Khera
317eec2790 allow kube app to scale 0 (#10909) 2024-01-08 08:31:31 +13:00
Prabhat Khera
7a1893f864 fix showing env var values (#10908) 2024-01-08 08:26:20 +13:00
Chaim Lev-Ari
c7125266f6 fix(registries): retag image [EE-6456] (#10836) 2024-01-05 18:02:09 -03:00
matias-portainer
69271c9d59 fix(docker/images): check for empty tags EE-6256 (#10531) 2024-01-05 17:33:42 -03:00
andres-portainer
717f0978d9 fix(tls): set the correct scheme for Docker clients EE-6514 (#10917) 2024-01-05 15:24:29 -03:00
Ali
abf517de28 refactor(app): migrate app summary section [EE-6239] (#10910) 2024-01-05 15:42:36 +13:00
matias-portainer
7a4314032a fix(docker/console): avoid resizing console when inactive EE-5370 (#10292) 2024-01-04 13:01:52 -03:00
andres-portainer
791c21f643 fix(swarm): retrieve the node names for the image list EE-6401 (#10879) 2024-01-04 10:28:24 -03:00
Chaim Lev-Ari
eb5975a400 docs(dashboard): update link for swarm node [EE-6318] (#10833)
Co-authored-by: holysoles <holysoles97@gmail.com>
2024-01-04 17:02:36 +07:00
Chaim Lev-Ari
400a80c07d chore(deps): upgrade to msw v2 [EE-6489] (#10911) 2024-01-04 16:57:21 +07:00
Matt Hook
ecd603db8c fix(docker-networks): use Network icon for networks [EE-6507] (#10913) 2024-01-04 18:54:04 +13:00
Chaim Lev-Ari
95358c204b chore(deps): upgrade docker-types [EE-6491] (#10905) 2024-01-03 16:55:45 +07:00
Ali
9fc7187e24 refactor(app): placement form section [EE-6386] (#10818)
Co-authored-by: testa113 <testa113>
2024-01-03 11:00:50 +13:00
Ali
2d77e71085 refactor(app): migrate-autoscaling [EE-6387] (#10709)
* refactor(app): migrate-autoscaling [EE-6387]
2024-01-03 10:42:39 +13:00
Ali
6da71661d5 refactor(app): migrate replicas form section [EE-6238] (#10705)
Co-authored-by: testa113 <testa113>
2024-01-03 10:27:38 +13:00
Ali
58da51f767 refactor(app): migrate deployment type section [EE-6237] (#10704)
Co-authored-by: testa113 <testa113>
2024-01-03 10:04:08 +13:00
Ali
947ba4940b refactor(app): migrate resource reservations [EE-6236] (#10695)
* refactor(app): migrate resource reservations [EE-6236]
2024-01-03 10:03:33 +13:00
Ali
e07ee05ee7 refactor(app): persisted folders form section [EE-6235] (#10693)
* refactor(app): persisted folder section [EE-6235]
2024-01-03 09:46:26 +13:00
Ali
7a2412b1be refactor(app): migrate configmap and secret form sections [EE-6233] (#10528)
* refactor(app): migrate configmap and secret form sections [EE-6233]
2024-01-03 09:07:11 +13:00
Matt Hook
391b85da41 fix(lib): update binaries and modules for CVEs [EE-6457] 2024-01-03 08:58:13 +13:00
Prabhat Khera
e412958dcc chore(build): exclude draft PRs [EE-5872] (#9987)
* exclude draft PRs
2024-01-03 08:25:35 +13:00
Ali
488393007f refactor(app): migrate env var form section [EE-6232] (#10499)
* refactor(app): migrate env var form section [EE-6232]

* allow undoing delete in inputlist
2024-01-03 08:17:54 +13:00
matias-portainer
6228314e3c fix(oauth): show asterisks placeholder in secret key input field EE-5664 (#10761) 2024-01-02 12:19:15 -03:00
Chaim Lev-Ari
ba19aab8dc refactor(registries): migrate repos table to react [EE-6451] (#10830) 2024-01-02 14:04:15 +07:00
Chaim Lev-Ari
3ae430bdd8 chore(build): remove eslint plugin [EE-6432] (#10773) 2024-01-02 13:42:48 +07:00
Chaim Lev-Ari
faa7180536 docs(api): default to pascal case for property name [EE-6471] (#10860) 2024-01-02 13:30:02 +07:00
Chaim Lev-Ari
a1519ba737 chore(deps): upgrade axios [EE-6488] (#10885)
Co-authored-by: Matt Hook <hookenz@gmail.com>
2024-01-02 13:26:54 +07:00
Chaim Lev-Ari
4c226d7a17 fix(templates): separate template views filters [EE-6397] (#10711) 2024-01-02 13:25:26 +07:00
Chaim Lev-Ari
82951093b5 chore(ci): run lint and test on all pkgs [EE-6201] (#10481) 2024-01-02 10:59:49 +07:00
Matt Hook
2e15cad048 fix(postcss): update postcss to 8.4.32 [EE-6490] 2023-12-29 06:39:53 +13:00
Matt Hook
27e997fe0d update go-get and x/crypto (#10893) 2023-12-28 07:54:41 +13:00
Matt Hook
6a4cfc8d7c chore(libs): update go libs and hide passwords/keys [EE-6496] (#10889) 2023-12-28 05:23:25 +13:00
Matt Hook
ebac0b9da2 upgrade golang and other dependant binaries (#10888) 2023-12-27 10:42:35 +13:00
andres-portainer
e3c5cd063b fix(chisel): fix a nil pointer dereference EE-6481 (#10871) 2023-12-22 11:36:01 -03:00
Chaim Lev-Ari
2b73116284 fix(templates): add host file entry [EE-6461] (#10839) 2023-12-21 15:56:02 +07:00
Prabhat Khera
d2ccb10972 add border to tooltip and modal in high contrast theme (#10834) 2023-12-20 08:55:00 +13:00
Prabhat Khera
6ede9f8cc3 disable html5 validation (#10844) 2023-12-20 08:54:00 +13:00
Prabhat Khera
6b07c874fc revert #10765 (#10870) 2023-12-19 14:19:24 +13:00
Ali
e84dd27e88 feat(cache): default to off [EE-6293] (#10867)
Co-authored-by: testa113 <testa113>
2023-12-19 12:13:44 +13:00
Matt Hook
5f1f797281 remove deprecated random seed and other minor staticcheck errors (#10851) 2023-12-18 11:48:41 +13:00
Ali
52fe09d0b1 fix(stacks): remove deployed version column [EE-6346] (#10859)
Co-authored-by: testa113 <testa113>
2023-12-18 11:39:38 +13:00
Matt Hook
e687cee608 ignore, remove or comment out unused code. Enable unused linter (#10743) 2023-12-18 10:28:15 +13:00
Matt Hook
8396ff068d enable gosimple linter (#10744) 2023-12-18 10:27:24 +13:00
Ali
d98fc1238e fix(git): stacks deployed version [EE-6346] (#10852)
Co-authored-by: testa113 <testa113>
2023-12-15 16:55:39 +13:00
Dakota Walsh
0ddf84638f fix(kubernetes): deprecate old configurations api EE-5571 (#10837)
* fix(kubernetes): deprecate old configurations api EE-5571

* fix doc variable type
2023-12-15 09:04:08 +13:00
Matt Hook
0b9407f0a6 close db before restore. fix log (#10826) 2023-12-14 12:01:05 +13:00
Oscar Zhou
e4d71d858d fix(setting/ssl): cert files are optional to upload [EE-6139] (#10776) 2023-12-13 23:20:19 +13:00
Chaim Lev-Ari
25741e8c4c feat(edge): sort waiting room table [EE-6259] (#10577) 2023-12-13 11:10:29 +02:00
Prabhat Khera
32d8dc311b fix cpu parsing logic (#10808) 2023-12-12 15:35:36 +13:00
Dakota Walsh
6ff6fd7f75 fix(swagger): custom template create docs EE-6428 (#10807) 2023-12-11 10:04:23 +13:00
Matt Hook
41b73fe2ae close the db before backup for windows shares and better error handling (#10810) 2023-12-08 15:24:23 +13:00
Prabhat Khera
fb3b00de41 fix(UI): remember backup settings tab selection [EE-6347] (#10765)
* remember backup settings tab selection

* address review comments
2023-12-08 15:17:27 +13:00
Prabhat Khera
0f9b91a15f disable create access btn if there is no team or user (#10766) 2023-12-08 14:19:43 +13:00
Dakota Walsh
79f3e1b04b fix(backup ui): minor typo on backup page EE-6348 (#10716) 2023-12-08 13:22:41 +13:00
matias-portainer
56022ab7b1 fix(stacks): allow editing custom templates before stack deployment EE-6380 (#10712) 2023-12-07 09:42:18 -03:00
Ali
4e8b371fb7 fix(gitops): clean trailing slash [EE-6346] (#10777)
Co-authored-by: testa113 <testa113>
2023-12-07 13:43:01 +13:00
Ali
a2d6d6002c fix(app): update sliders when limits are known [EE-5933] (#10768)
Co-authored-by: testa113 <testa113>
2023-12-07 12:11:44 +13:00
Chaim Lev-Ari
dabcf4f7db feat(custom-templates): migrate create view to react [EE-6400] (#10715) 2023-12-06 14:11:02 +01:00
Prabhat Khera
bd5ba7b5d0 fix(kube): configmaps and secrets from envFrom in the app detail screen [EE-6282] (#10742)
* fix configmaps and secrets from envFrom

* adress review comments
2023-12-06 16:02:26 +13:00
James Carppe
1d279428a7 Update bug template for 2.19.4 (#10763) 2023-12-06 12:05:53 +13:00
Chaim Lev-Ari
8ee0c0cf27 fix(images): sort by tags [EE-6410] (#10740) 2023-12-04 08:47:28 +02:00
Chaim Lev-Ari
2a18c9f215 fix(edge/templates): fix issues with git templates [EE-6357] (#10679) 2023-12-04 08:46:44 +02:00
Ali
974378c9b5 fix(gitops): correct commit hash link [EE-6346] (#10723) 2023-12-04 11:18:15 +13:00
Matt Hook
eb23818f83 fix(rollback): reimplement rollback feature [EE-6367] (#10721) 2023-12-04 09:12:41 +13:00
Ali
8f4d6e7e27 fix(app): shift external to the top [EE-6392] (#10719)
Co-authored-by: testa113 <testa113>
2023-12-04 07:43:53 +13:00
Oscar Zhou
5c7f6aab66 fix(docker/image): swarm image list issue [EE-6374] (#10710) 2023-12-01 16:49:31 +13:00
Chaim Lev-Ari
3cf36b0e93 fix(app/templates): show default url in settings [EE-6393] (#10706) 2023-11-30 07:18:15 +02:00
Chaim Lev-Ari
7a9436dad7 fix(edge/stacks): clear templates values on change method [EE-6390] (#10707) 2023-11-30 07:13:01 +02:00
cmeng
5c59c53e91 fix(password): force change password EE-6382 (#10708) 2023-11-30 17:46:57 +13:00
Ali
e3a995d515 fix(pvc): show access modes [EE-5581] (#10554)
Co-authored-by: testa113 <testa113>
2023-11-30 09:48:55 +13:00
Ali
87b486b798 fix(PVC): access modes match storage class capability [EE-5580] (#10550) 2023-11-30 09:48:14 +13:00
cmeng
92c18843b2 fix(wizard): count swarm agent as local environment EE-6215 (#10684) 2023-11-30 08:53:56 +13:00
Ali
450c167461 fix(cache): exclude reqs that accept yaml [EE-6381] (#10696)
Co-authored-by: testa113 <testa113>
2023-11-29 11:45:10 +13:00
Ali
bdcb003a32 fix(app): dont validate stack name [EE-6379] (#10701)
Co-authored-by: testa113 <testa113>
2023-11-29 11:44:33 +13:00
Chaim Lev-Ari
c40931b31c fix(templates): show type selector [EE-6370] (#10694) 2023-11-28 15:40:22 +02:00
Matt Hook
db46dc553f fix(backups): fix rollback feature [EE-6367] (#10691) 2023-11-28 11:12:17 +13:00
Chaim Lev-Ari
76bcdfa2b8 fix(edge/templates): fix issues [EE-6328] (#10656) 2023-11-27 09:56:15 +02:00
cmeng
140ac5d17c fix(logout): clean user data when logout EE-6368 (#10690) 2023-11-27 17:21:55 +13:00
Ali
2fe965942a fix(kubeClient): get standard client [EE-6376] (#10692)
Co-authored-by: testa113 <testa113>
2023-11-27 16:48:47 +13:00
cmeng
dc574af734 fix(container): runtime and resources issues EE-6306 (#10611) 2023-11-27 11:56:44 +13:00
Ali
1bcbfb8213 fix(cache): set as true for a new admin [EE-6293] (#10689)
Co-authored-by: testa113 <testa113>
2023-11-27 10:19:08 +13:00
Oscar Zhou
6bec4cdecc fix(edgestack): set retry deployment (#10676) 2023-11-24 13:45:10 +13:00
Ali
04c1c7d8fb fix(cache): cache fixes [EE-6293] (#10681)
* fix(cache): default cache to on for new users [EE-6293]

* clear cache to transition terminating namespace

* add rq requests back to the namespace view

---------

Co-authored-by: testa113 <testa113>
2023-11-24 11:27:52 +13:00
Ali
2f91315ac7 fix(namespace): handle undefined registry options [EE-6366] (#10683)
Co-authored-by: testa113 <testa113>
2023-11-24 10:58:32 +13:00
andres-portainer
a4b17d2548 fix(gitops): change the condition that checks if the environment is online EE-6321 (#10665) 2023-11-23 11:54:50 -03:00
Chaim Lev-Ari
26953d0b15 fix(templates): change default url [EE-6363] (#10680) 2023-11-23 03:41:22 +02:00
cmeng
13d1fc63ff fix(stack): sync ee code to ce EE-5960 (#10642) 2023-11-23 09:17:12 +13:00
Ali
a4926e5237 fix(namespace): create page layout [EE-6385] (#10675) 2023-11-22 15:37:11 +13:00
James Carppe
936a71ee00 Update bug template for 2.19.3 (#10674) 2023-11-22 14:25:22 +13:00
Ali
4096bb562d feat(cache): introduce cache option [EE-6293] (#10672)
Co-authored-by: testa113 <testa113>
2023-11-22 14:21:07 +13:00
cmeng
57ed6ae6a6 fix(edge-stack): parse docker compose multi lines json output EE-6317 (#10627) 2023-11-20 22:54:28 +13:00
Chaim Lev-Ari
ad5a17ac34 feat(edge/updates): schedule time changes [EE-5975] (#10458) 2023-11-20 10:24:54 +02:00
Chaim Lev-Ari
436da01bce feat(auth): save jwt in cookie [EE-5864] (#10527) 2023-11-20 09:35:03 +02:00
Ali
ecce501cf3 Revert "feat(cache): introduce cache option [EE-6293] (#10641)" (#10658)
This reverts commit 2c032f1739.
2023-11-20 15:08:19 +13:00
Ali
2c032f1739 feat(cache): introduce cache option [EE-6293] (#10641) 2023-11-20 10:22:48 +13:00
cmeng
fffc7b364e fix(container): Unable to create container with webhook EE-6313 (#10619) 2023-11-17 14:35:47 +13:00
andres-portainer
0b5b8971b1 fix(gitops): handle the local environment in isEnvironmentOnline() EE-6321 (#10631) 2023-11-16 09:40:42 -03:00
cmeng
be09c5e346 fix(volumes): Volumes browse button spacing issue EE-6323 (#10633) 2023-11-16 16:25:17 +13:00
cmeng
d089dfbca0 fix(container): fix various creating container issues EE-6287 (#10595)
* fix(container): show placeholder for image field EE-6287

* fix(container): correct query params for search button field EE-6287

* fix(container): use btoa to encode registry credential EE-6287

* fix(container): allow creating non-existing option EE-6287

* fix(ui/forms): typeahead component

* fix(container): select the default registry EE-6287

* fix(container): always enable deploy button when always pull is off EE-6287

* fix(container): reset command fields outside current event to avoid validation on broken values EE-6287

* fix(container): query registry with endpoint ID param EE-6287

---------

Co-authored-by: Chaim Lev-Ari <chaim.levi-ari@portainer.io>
2023-11-16 08:50:23 +13:00
Chaim Lev-Ari
e43d076269 feat(edge/templates): introduce edge specific settings [EE-6276] (#10609) 2023-11-15 14:43:18 +02:00
Chaim Lev-Ari
68950fbb24 feat(edge/templates): introduce custom templates [EE-6208] (#10561) 2023-11-15 10:45:07 +02:00
Chaim Lev-Ari
a0f583a17d fix(containers): align switches [EE-6314] (#10616) 2023-11-15 09:34:08 +02:00
Chaim Lev-Ari
51474262eb fix(access-control): show only environment users [EE-6315] (#10614) 2023-11-15 09:33:29 +02:00
Matt Hook
3525a1af77 fix(kube): change advanced deployment label [EE-6310] (#10626)
* change namespace label to deploy to

* fix var typo
2023-11-15 11:35:39 +13:00
Chaim Lev-Ari
e1e90c9c1d feat(edge/templates): introduce edge app templates [EE-6209] (#10480) 2023-11-14 14:54:44 +02:00
Chaim Lev-Ari
95d96e1164 fix(ui): parse slider value correctly [EE-6225] (#10484) 2023-11-14 13:17:25 +02:00
Chaim Lev-Ari
99b39da03d refactor(edge/groups): migrate view to react [EE-4683] (#10592) 2023-11-14 12:57:27 +02:00
Chaim Lev-Ari
1f2f4525e3 feat(ui/buttons): introduce Add and Delete buttons [EE-6296] (#10585) 2023-11-14 12:36:15 +02:00
James Carppe
66635ba6b1 Updated versions in bug report template (#10620)
LGTM
2023-11-13 07:07:44 +05:30
yi-portainer
3630aab820 * remove line break 2023-11-13 14:18:52 +13:00
Matt Hook
3c8c2118d4 update namespace section for helm (#10610) 2023-11-13 09:37:52 +13:00
Chaim Lev-Ari
d6ac29b498 fix(edge/stacks): remove parentheses [EE-6277] (#10560) 2023-11-09 09:55:54 +02:00
Prabhat Khera
e73b7fe0fd fix(kubernetes): clear user token from kube token cache on logout + update cluster rolebindings for user on change of team/user authorization [EE-6298] (#10598)
* clear user token from kube token cache on logoug + updates cluster role bindings for service accounts on change user/teams authorizations
2023-11-09 14:33:23 +13:00
Prabhat Khera
e761a00098 fix(kubernetes): URL form validation for advance deployment [EE-6280] (#10607) 2023-11-09 13:26:42 +13:00
Oscar Zhou
9041880bdb fix(container): assign container domain name (#10605) 2023-11-09 10:19:28 +13:00
Ali
e4ddd8048a fix(app): disable deploy when there are no namespaces [EE-6295] (#10606)
Co-authored-by: testa113 <testa113>
2023-11-08 03:22:41 +00:00
Oscar Zhou
e6ef913bb1 fix(docker/swarm): suppress no such container logs (#10604) 2023-11-08 11:43:42 +13:00
Matt Hook
3fd696d6b5 switch to filter after upgrading helm binary (#10596) 2023-11-06 13:32:14 +13:00
Ali
24c9959ca4 fix(app): hide services section when there are no namespaces [EE-6295] (#10588)
Co-authored-by: testa113 <testa113>
2023-11-05 17:37:48 +00:00
Chaim Lev-Ari
e72671e4ab fix(edge/updates): hide sidebar item when disabled [EE-6294] (#10582) 2023-11-05 12:45:56 +02:00
Prabhat Khera
47c9e498f9 fix validation fro custom template (#10587) 2023-11-03 11:39:44 +13:00
Ali
0c323b48e7 fix(nodes): restrict nodes details from standard user [EE-6125] (#10586)
Co-authored-by: testa113 <testa113>
2023-11-02 19:02:19 +00:00
Prabhat Khera
103d908e63 fix(users): hide admin users for non admins from user list API [EE-6290] (#10580)
* hide admin users for non admins from user list API

* address review comments
2023-11-02 16:08:17 +13:00
cmeng
2972022523 fix(version): show build info EE-6278 (#10578) 2023-11-02 11:04:11 +13:00
Oscar Zhou
4ffeefd267 feat(security): add docker scout pr github action flow (#10557) 2023-11-02 09:34:24 +13:00
Prabhat Khera
c8bdf21d07 fix(kubernetes): validation for advance deployments [EE-6280] (#10574) 2023-11-02 08:50:12 +13:00
Prabhat Khera
b6f9777bbf fix custom template id on select (#10573) 2023-11-02 08:46:50 +13:00
Ali
f6b78312f4 fix(nodes): allow standard users to get kube endpoints [EE-6125] (#10572)
Co-authored-by: testa113 <testa113>
2023-11-01 19:08:38 +00:00
Chaim Lev-Ari
948486df77 fix(services): update service [EE-6275] (#10559) 2023-11-01 12:27:38 +02:00
cmeng
600c8a3025 fix(volumes): fix broken volume browse button EE-6274 (#10566) 2023-11-01 14:15:54 +13:00
cmeng
8daf77c3b6 fix(log-viewer): unable to view container logs EE-6273 (#10555) 2023-11-01 12:00:05 +13:00
matias-portainer
8bb5129be0 feat(nomad): remove nomad from UI EE-6060 (#10509) 2023-10-31 15:27:20 -03:00
Ali
1140804fe9 fix(app): sync showSystem between stacks and apps tables [EE-6216] (#10532) 2023-10-30 19:41:41 +00:00
Prabhat Khera
7d868d1dc9 hide stacks tab if stack feature is disabled (#10551) 2023-10-30 14:13:36 +13:00
andres-portainer
247f358b94 fix(code): revert omitempty optimization EE-6269 (#10548) 2023-10-27 17:33:04 -03:00
matias-portainer
f10356641a fix(edge/aeec): make edge id generator field mandatory EE-6010 (#10545) 2023-10-27 10:35:10 -03:00
LP B
9e60723e4d fix(app/logout): always perform API logout + make API logout route public [EE-6198] (#10448)
* feat(api/logout): make logout route public

* feat(app/logout): always perform API logout on /logout redirect

* fix(app): send a logout event to AngularJS when axios hits a 401
2023-10-27 14:44:05 +02:00
Ali
47fa1626c6 fix(app): don't attach all ingresses to app [EE-5686] (#10537) 2023-10-27 16:59:45 +13:00
Prabhat Khera
26036c05f2 fix(kubernetes): remove unique check from kubernetes stacks [EE-6170] (#10542) 2023-10-27 15:41:02 +13:00
Matt Hook
8ee718f808 chore(binaries): upgrade binaries [EE-6253] (#10529) 2023-10-27 15:40:06 +13:00
cmeng
30e4b3e68c fix(edge-stack): remove text info for relative path EE-6228 (#10541) 2023-10-27 14:53:20 +13:00
cmeng
0d56504268 fix(settings): disable save button when revert changes EE-6263 (#10543) 2023-10-27 13:47:08 +13:00
Ali
6a5f44b5ba fix(errors): display kube confgimap and secret errors [EE-5558] (#10539) 2023-10-27 10:56:03 +13:00
cmeng
3964852fda fix(container): hide capabilities tab EE-6258 (#10540) 2023-10-26 15:44:31 +13:00
Ali
403fdf7ce3 fix(nodes): disable select [EE-4692] (#10538)
Co-authored-by: testa113 <testa113>
2023-10-25 23:59:01 +01:00
Ali
afa3e7477b fix(toggle): update cursor style and color transition speed [EE-6229] (#10534)
Co-authored-by: testa113 <testa113>
2023-10-25 23:17:18 +01:00
Ali
d9effb3597 fix(nodes): fix nodes datatable width [EE-4962] (#10533)
Co-authored-by: testa113 <testa113>
2023-10-25 20:42:36 +01:00
andres-portainer
ee80e3d252 fix(edge): fix frontend issues with omitempty EE-6260 (#10536) 2023-10-25 15:51:39 -03:00
matias-portainer
824706e4e9 fix(ui): label GPU functionality as NVIDIA only EE-6204 (#10522) 2023-10-25 14:48:22 -03:00
Chaim Lev-Ari
09f9c09706 chore(ci): separate tests from CI (#10524) 2023-10-25 07:52:04 +03:00
Prabhat Khera
577eef5de0 fix stack name placeholder and some css styling (#10523) 2023-10-25 08:59:10 +13:00
andres-portainer
ae1726cece feat(performance): performance optimizations EE-6042 (#10520) 2023-10-24 13:55:11 -03:00
andres-portainer
e4e66dac9c fix(gitops): only attempt to redeploy when the environment appears to be online EE-6182 (#10464) 2023-10-24 11:20:45 -03:00
Steven Kang
08fdebfbd9 feat(ci): introduce GH Actions for Portainer CE (#10419)
Co-authored-by: Chaim Lev-Ari <chaim.levi-ari@portainer.io>
2023-10-24 13:30:33 +03:00
Matt Hook
860890046d fix(registry): remove k8s registry secrets when registries are removed [EE-5768] (#10369) 2023-10-24 09:24:09 +13:00
Ali
96ead31a8d fix(kubeapi): fix ts api error handling [EE-5558] (#10488)
* fix(kubeapi): fix ts api error handling [EE-5558]

* use portainer errors for mapped functions

* don't parse long patch responses

* allow nested kube error that's thrown to bubble up

---------

Co-authored-by: testa113 <testa113>
2023-10-23 20:52:40 +01:00
andres-portainer
6c55cac52a feat(code): equalize the code with EE EE-6218 (#10518) 2023-10-23 15:52:37 -03:00
Ali
e110856003 fix(namespace): remove duplicate 'no registry' text [EE-2226] (#10519)
Co-authored-by: testa113 <testa113>
2023-10-23 19:49:24 +01:00
Chaim Lev-Ari
10c3ed42f0 refactor(custom-templates): migrate list component to react [EE-6206] (#10440) 2023-10-23 20:00:50 +03:00
Chaim Lev-Ari
14129632a3 refactor(app-templates): convert list to react [EE-6205] (#10439) 2023-10-23 19:04:18 +03:00
Chaim Lev-Ari
1fa63f6ab7 refactor(docker/services): migrate service tasks to react [EE-4676] (#10328) 2023-10-23 13:52:49 +03:00
Chaim Lev-Ari
70455320be fix(docker/volumes): Add volume typo [EE-6226] (#10483) 2023-10-23 13:31:59 +03:00
Chaim Lev-Ari
b933bee95e feat(docker/networks): migrate networks datatable to React [EE-4670] (#10351)
Co-authored-by: LP B <xAt0mZ@users.noreply.github.com>
2023-10-22 12:35:22 +03:00
Chaim Lev-Ari
0dc1805881 refactor(docker/services): convert services table to react [EE-4675] (#10289) 2023-10-22 12:32:05 +03:00
Chaim Lev-Ari
6b5c24faff refactor(custom-templates): migrate common-fields to react [EE-6207] (#10445) 2023-10-22 12:19:19 +03:00
Chaim Lev-Ari
1ad9488ca7 refactor(templates): migrate template item to react [EE-6203] (#10429) 2023-10-19 21:09:15 +02:00
Chaim Lev-Ari
d970f0e2bc refactor(containers): migrate create view to react [EE-2307] (#9175) 2023-10-19 13:45:50 +02:00
cmeng
bc0050a7b4 fix(user-token): prevent admin read tokens of other admins EE-5858 (#10489) 2023-10-19 16:23:14 +13:00
Prabhat Khera
03155685ab fix toggle colors (#10503) 2023-10-19 15:01:31 +13:00
Prabhat Khera
7e4d113fda fix libhelm error message (#10502) 2023-10-19 13:20:49 +13:00
Oscar Zhou
7c91780eb7 fix(edge): introduce pause and rollback status [EE-5992] (#10465) 2023-10-19 11:26:02 +13:00
Ali
877dc1e236 fix(namespace): update no registries text [EE-2226] (#10501)
Co-authored-by: testa113 <testa113>
2023-10-18 22:57:29 +01:00
Prabhat Khera
56f3bd8417 add name field for helm install in advance deployments (#10493) 2023-10-18 11:37:03 +13:00
Ali
776be2e022 fix(sidebar): high contrast styles, single option link [EE-5666] (#10485) 2023-10-16 21:23:23 +01:00
Ali
0e47f22c0a refactor(cluster): migrate nodes datatable to react [EE-4962] (#10459)
Co-authored-by: testa113 <testa113>
2023-10-16 21:19:08 +01:00
andres-portainer
b346fd7f39 fix(store): fix StoreIsUpdating() to properly set the state EE-6227 (#10486) 2023-10-16 16:32:30 -03:00
Prabhat Khera
35448c7f48 fix helm install (#10479) 2023-10-17 07:50:13 +13:00
Ali
07ec2ffe5e fix(namespace): create ns qa feedback [EE-2226] (#10474) 2023-10-16 19:15:44 +01:00
Prabhat Khera
bcb3f918d1 some minor UI fixes (#10475) 2023-10-16 14:08:55 +13:00
Prabhat Khera
7840e0bfe1 feature(kubernetes): stack name made optional & add toggle to disable stack in kubernetes [EE-6170] (#10436) 2023-10-16 14:08:06 +13:00
Chaim Lev-Ari
44d66cc633 fix(docker/secrets): allow navigating to secret item page [EE-6164] (#10382) 2023-10-15 09:33:27 +03:00
Matt Hook
148bd4d997 chore:(kubeclient): refactor kubeclient middleware and endpoints [EE-5028] (#10423) 2023-10-13 13:43:36 +13:00
Matt Hook
7c4c985247 upgrade some badge components to match EE (#10451) 2023-10-13 03:10:16 +13:00
Chaim Lev-Ari
57c45838d5 fix(edge/updates): allow group search [EE-6179] (#10408) 2023-10-12 08:30:23 +03:00
Ali
5a73605df2 fix(sidebar): consistent font weight [EE-5666] (#10461) 2023-10-12 01:59:46 +01:00
Prabhat Khera
ff5b311eee fix(helm): fix helm move to advance deployments issues [EE-5999] (#10453)
* fix helm move to adv deployments
2023-10-12 11:02:09 +13:00
Ali
7218eb0892 feat(namespace): migrate create ns to react [EE-2226] (#10377) 2023-10-11 20:32:02 +01:00
Prabhat Khera
31bcba96c6 feature(UI): toggle styling changes [EE-4602] (#10373) 2023-10-12 07:34:38 +13:00
Ali
6a5f5aa424 fix(sidebar): qa feedback [EE-5666] (#10452) 2023-10-11 19:32:52 +01:00
matias-portainer
da5a4d6714 fix(swarm/services): avoid sending credSpec object when empty EE-6178 (#10441) 2023-10-11 11:30:23 -03:00
Chaim Lev-Ari
35dfde70de refactor(ui/page-header): make docs url explicit [EE-5966] (#10411) 2023-10-11 10:38:57 +03:00
Chaim Lev-Ari
9e57530bde fix(build): handle warning about userId [EE-5612] (#10444) 2023-10-11 10:30:14 +03:00
Chaim Lev-Ari
5c37ed328f refactor(docker/volumes): migrate table to react [EE-4677] (#10312) 2023-10-11 10:27:42 +03:00
Chaim Lev-Ari
8e1417b4e9 refactor(docker/containers): remove EndpointProvider from container service [EE-6180] (#10392) 2023-10-11 10:26:44 +03:00
Chaim Lev-Ari
b80fcb0467 fix(docker/stacks): show orphaned stacks option [EE-6149] (#10346) 2023-10-11 10:24:35 +03:00
cmeng
66ca73f98b fix(edge-stack): sync CE code with EE EE-6163 (#10437) 2023-10-11 18:11:12 +13:00
Ali
a0dbabcc5f feat(sidebar): update menu structure [EE-5666] (#10418) 2023-10-09 19:23:12 +01:00
Prabhat Khera
b468070945 feature(helm): move helm charts inside advance deployments (create from manifest) [EE-5999] (#10395) 2023-10-09 11:20:44 +13:00
Oscar Zhou
9885694df6 fix(filesys): update stack version methods [EE-6190] (#10406) 2023-10-06 09:08:22 +13:00
Chaim Lev-Ari
95f3cf6e5b refactor(server): use httperror.NewError instead of struct [EE-6189] (#10398) 2023-10-05 11:26:24 +03:00
Chaim Lev-Ari
da346cba60 chore(deps): update ts and more deps [EE-5756] (#10409) 2023-10-05 11:25:35 +03:00
Chaim Lev-Ari
5f9687a361 fix(edge/waitingroom): hide sidebar when disabled [EE-6003] (#10343) 2023-10-05 10:31:08 +03:00
Chaim Lev-Ari
20823a7f27 chore(deps): upgrade golangci [EE-5685] (#10410) 2023-10-04 08:50:59 +03:00
Chaim Lev-Ari
9bf2957ea7 feat(docker/images): show used tag correctly [EE-5396] (#10305) 2023-10-03 15:55:23 +03:00
Ali
b895e88075 fix(teasers): add teaser message full stops [EE-6035] (#10401) 2023-10-02 21:23:00 +01:00
matias-portainer
671f74ce0d fix(edge/groups): include only user trusted endpoints in endpoint count EE-5964 (#10378) 2023-10-02 11:37:39 -03:00
cmeng
56ab19433a fix(websocket): abort websocket when logout EE-6058 (#10372) 2023-09-29 12:13:09 +13:00
Matt Hook
9440aa733d support proxy for helm repo validation (#10358) 2023-09-29 10:55:49 +13:00
LP B
ada6b31f69 fix(docker/container): container logs viewer error when logging is disabled (#10384)
* fix(docker/container-logs): invalid string breadcrumb

* fix(docker/container): let docker select the logging driver by default on container create

* fix(docker/container-logs): information panel in container logs when logging is disabled

* fix(docker/container): dont include HostConfig.LogConfig if no driver is selected
2023-09-28 15:53:52 +02:00
Ali
d678b155ba fix(teasers): updated muted styles from qa feedback [EE-6035] (#10390)
* fix(teasers): updated muted styles from qa feedback [EE-6035]
2023-09-28 11:32:58 +01:00
Prabhat Khera
99625cd35f fix team lead access to view user names (#10388) 2023-09-28 12:40:54 +13:00
Chaim Lev-Ari
95ca1d396b fix(docker/services): show cred spec configs [EE-5276] (#10083) 2023-09-27 07:57:47 +03:00
Chaim Lev-Ari
e28322459a fix(stacks): mark stack as start after autoupdate [EE-6165] (#10376) 2023-09-27 07:53:33 +03:00
Prabhat Khera
3ff2f64930 fix(authorization): disable user list api call if not authorised [EE-5825] (#10379)
* disable user list api call if not authorised

* fix tests

* fix lint issues
2023-09-27 10:12:30 +13:00
Ali
702391cf88 remove apostrophe from tooltip (#10386)
Co-authored-by: testa113 <testa113>
2023-09-26 21:25:08 +01:00
LP B
d437cde046 fix(docker/container): missing return statement when preparing container config (#10383) 2023-09-26 14:09:38 +02:00
Chaim Lev-Ari
7acde18930 feat(containers): migrate labels tab to react [EE-5212] (#10348) 2023-09-26 13:54:45 +03:00
cmeng
b4b44e6fa4 fix(edge-config): allow empty filter type EE-5962 (#10381) 2023-09-26 13:49:25 +13:00
Chaim Lev-Ari
2dfa4a7c45 refactor(containers): migrate restart policy tab to react [EE-5213] (#10347) 2023-09-25 20:40:26 +03:00
Chaim Lev-Ari
3d19c46326 style(kubernetes): disable autoFocus warning [EE-5752] (#10368) 2023-09-25 20:13:31 +03:00
Chaim Lev-Ari
57e04c3544 refactor(containers): migrate caps tab to react [EE-5215] (#10366) 2023-09-25 19:36:50 +03:00
Chaim Lev-Ari
9dde610da3 fix(docker/containers): create container with bridge network [EE-6160] (#10365) 2023-09-25 19:35:54 +03:00
LP B
26cb75def9 feat(app/home): tooltip aside edge agent version on mismatch with Portainer version (#10287)
* feat(app/home): tooltip aside edge agent version on mismatch with Portainer version

* fix(app/home): split agent and edge version display + display warning for agents before 2.15
2023-09-25 11:56:08 +02:00
Prabhat Khera
3c4660bbf3 fix(permissions): non admin access to view users [EE-5825] (#10352)
* fix non admin access to view users

* review comments and fix tests
2023-09-25 09:08:26 +13:00
Ali
13c48ab961 fix(be-teaser): mute styles [EE-6035] (#10349) 2023-09-24 19:56:09 +01:00
Chaim Lev-Ari
ffac83864d refactor(containers): migrate resources tab to react [EE-5214] (#10355) 2023-09-24 15:31:06 +03:00
Prabhat Khera
ec091efe3b fix deadlock situation (#10360) 2023-09-22 16:06:20 +12:00
cmeng
fb7a2fbbe6 fix(stack): fix edit git stack validation EE-5855 (#10339) 2023-09-22 10:09:24 +12:00
matias-portainer
dfce48cd5e fix(stacks): check properly if endpoint id is defined in the stacks object EE-6118 (#10302) 2023-09-21 10:12:43 -03:00
Chaim Lev-Ari
2b47b84e5e feat(docker/containers): migrate network tab to react [EE-5210] (#10344) 2023-09-21 14:02:02 +03:00
Chaim Lev-Ari
e92f067e42 refactor(containers): migrate volumes tab to react [EE-5209] (#10284) 2023-09-21 05:31:00 +03:00
Chaim Lev-Ari
16ccf5871e refactor(docker/containers): migrate env vars to react [EE-5211] (#10345) 2023-09-21 04:11:18 +03:00
cmeng
54112b56f2 feat(edge-config): support edge config for group EE-5962 (#10329) 2023-09-21 11:22:44 +12:00
LP B
a66942aa5a fix(app/stacks): swarm stacks incorrectly marked as orphaned (#10319) 2023-09-20 12:40:08 +02:00
Ali
c18504d6f1 fix(cluster): make angular refresh env [EE-5524] (#10315)
Co-authored-by: testa113 <testa113>
2023-09-20 19:33:43 +12:00
Chaim Lev-Ari
25d5e62f5c refactor(kube/apps): migrate stacks table to react [EE-4661] (#10091) 2023-09-20 09:04:26 +03:00
James Carppe
a5f60c64ef Added 2.19.1 to list of versions in bug report template (#10338) 2023-09-20 07:48:35 +05:30
Matt Hook
d6d532473e allow libhelm to use forward proxy (#10331) 2023-09-19 18:07:51 +12:00
Chaim Lev-Ari
af7834174a fix(api): restore deleted apis [EE-6090] (#10267) 2023-09-19 13:44:48 +12:00
Prabhat Khera
14853f6da0 fix(kubernetes): kube env permissions when down [EE-5427] (#10327) 2023-09-19 08:57:27 +12:00
Oscar Zhou
cc37ccfe4d fix(db/migration): avoid fatal error from being overwritten (#10316) 2023-09-18 14:33:04 +12:00
Matt Hook
e3a4b7ad17 improved user update validation (#10321) 2023-09-18 12:29:04 +12:00
Dakota Walsh
0a02f6b02e fix(kubernetes): add prefix only when needed EE-6068 (#3915) (#10310) 2023-09-15 09:25:56 +12:00
Chaim Lev-Ari
dcdf5e1837 fix(edge/jobs): clear logs [EE-5923] (#10291) 2023-09-13 22:11:42 +01:00
Chaim Lev-Ari
bf85a8861d refactor(docker/swarm): migrate nodes table to react [EE-4672] (#10184) 2023-09-13 10:51:33 +01:00
Chaim Lev-Ari
fbdbd277f7 fix(docker/container): pass empty command and entrypoint [EE-6106] (#10285) 2023-09-13 10:47:13 +01:00
cmeng
0a80f4dc51 fix(backup): add chisel key to backup EE-6105 (#10283) 2023-09-13 09:01:27 +12:00
andres-portainer
5a0cb4d0e8 fix(gitops): avoid cancelling the auto updates for any error EE-5604 (#10294) 2023-09-12 17:53:01 -03:00
Oscar Zhou
f17da30d31 fix(db/init): check server version and db schema version (#10300) 2023-09-12 15:55:09 +12:00
Matt Hook
291625959b update logic to purge the cache, update the message when the environment can't be reached (#10298) 2023-09-12 13:52:09 +12:00
Prabhat Khera
4c16594a25 fix(security): added restrictions to see user names [EE-5825] (#10296)
* fix(security): added restrictions to see user names [EE-5825]

* use pluralize method
2023-09-12 13:15:34 +12:00
Chaim Lev-Ari
60477ae287 refactor(docker/networks): migrate macvlan nodes selector to react [EE-4669] (#10183) 2023-09-11 15:27:04 +01:00
Chaim Lev-Ari
09aa1d35a8 refactor(ui): remove unused tables [EE-4698] (#10215) 2023-09-11 15:26:22 +01:00
cmeng
7669a3c8c6 fix(settings): misaligned poll frequency selector EE-6081 (#10286) 2023-09-11 15:35:44 +12:00
Ali
dde4b95426 fix(cluster): faster submitting load times [EE-5524] (#10280)
* faster submitting load times

* scroll to selected tz option

---------

Co-authored-by: testa113 <testa113>
2023-09-11 10:52:00 +12:00
LP B
dfd415c62e fix(app/stacks): stacks incorrectly marked as orphaned (#10273) 2023-09-08 22:22:26 +02:00
Matt Hook
b40b305e63 fix(styles): improve styling of form-section-title [EE-5366] (#10250) 2023-09-08 13:40:09 +12:00
Chaim Lev-Ari
c8a1f0fa77 refactor(docker/stacks): migrate table to react [EE-4705] (#9956) 2023-09-07 15:59:59 +01:00
Chaim Lev-Ari
c3d266931f refactor(docker/services): convert service tasks table to react [EE-4674] (#10188) 2023-09-07 15:19:03 +01:00
Chaim Lev-Ari
c47a804c97 refactor(docker/secrets): migrate table to react [EE-4673] (#10185) 2023-09-07 15:15:22 +01:00
Chaim Lev-Ari
b15812a74d refactor(docker/containers): migrate networks table to react [EE-4665] (#10069) 2023-09-07 15:14:03 +01:00
matias-portainer
776f6a62c3 fix(authentication): allow nested whitespaces on AD OU names EE-5206 (#10260) 2023-09-07 11:02:57 -03:00
Chaim Lev-Ari
ae3e612a24 feat(docker/stacks): fold env vars by default [EE-5575] (#9957) 2023-09-07 14:45:59 +01:00
Ali
6a8ff7c076 fix(yaml): remove create message on edit views [EE-5356] (#10254)
Co-authored-by: testa113 <testa113>
2023-09-07 09:29:25 +12:00
andres-portainer
4a39122415 fix(code): remove code that is no longer necessary EE-6078 (#10256) 2023-09-05 22:35:16 -03:00
andres-portainer
c748385879 feat(transactions): remove the feature flag EE-6080 (#10257) 2023-09-05 20:27:20 -03:00
Oscar Zhou
e83aa4d88d fix(gomod): update golang version (#10255) 2023-09-06 10:29:58 +12:00
Matt Hook
91d2132264 prevent regular users changing their username (#10247) 2023-09-06 09:17:04 +12:00
Matt Hook
e5f7641e46 non-admins must supply existing passwd when changing passwd (#10249) 2023-09-06 08:26:32 +12:00
Ali
515b02813b feat(k8sconfigure): migrate configure to react [EE-5524] (#10218) 2023-09-06 04:06:36 +12:00
Oscar Zhou
0f1e77a6d5 fix(security): update dependency and binary version [EE-5798] (#10192) 2023-09-05 17:23:12 +12:00
Prabhat Khera
a02f9f1f07 fix(kubernetes): run group permission when endpoint is up [EE-5427] (#10121)
* update group access when env is down

* fix tests
2023-09-05 11:03:43 +12:00
Dakota Walsh
d75a8027a5 fix(security): block user access policies for non admins EE-5826 (#10243) 2023-09-05 09:17:55 +12:00
Dakota Walsh
6a08bbe7e9 fix(security): block non-admins from user info listing EE-5825 (#10241) 2023-09-05 09:17:05 +12:00
Chaim Lev-Ari
e82b34b775 refactor(docker/services): migrate scale form to react [EE-6057] (#10208) 2023-09-04 16:24:41 -03:00
Chaim Lev-Ari
f7366d9788 refactor(docker/containers): migrate commands tab to react [EE-5208] (#10085) 2023-09-04 19:07:29 +01:00
Chaim Lev-Ari
46e73ee524 refactor(docker/containers): migrate processes table to react [EE-4666] (#10081) 2023-09-04 17:05:01 +01:00
Chaim Lev-Ari
e5880b3e34 fix(edge): add background to table icons [EE-6020] (#10187) 2023-09-04 16:52:51 +01:00
Chaim Lev-Ari
0e2eb17220 chore(deps): upgrade tailwind and prettier [EE-5218] (#10068) 2023-09-04 16:20:36 +01:00
Chaim Lev-Ari
cb7377ead6 refactor(ui/datatables): allow datatable to globally filter on object value [EE-5824] (#9955) 2023-09-04 10:33:07 +01:00
Oscar Zhou
440f4e8dda fix(edge): stack associated no dynamic group being deployed [EE-5531] (#10224) 2023-09-04 17:04:45 +12:00
James Carppe
490e4ec655 Add 2.19.0 to bug report template (#10239) 2023-09-04 10:20:55 +05:30
Dakota Walsh
7be8619ab7 fix(search): Add noindex meta tag EE-5371 (#10220) 2023-09-04 07:45:44 +12:00
Chaim Lev-Ari
4a6b7e2654 fix(ui/switch): reduce label size [EE-3803] (#10019) 2023-09-03 10:26:38 +01:00
andres-portainer
8cc5e0796c feat(libhttp): move into the Portainer repository EE-5475 (#10231) 2023-09-01 19:27:02 -03:00
andres-portainer
090fa4aeb3 feat(libcrypto): move into the Portainer repository EE-5476 (#10230) 2023-09-01 17:27:19 -03:00
andres-portainer
9a234204fa chore(go): move go.mod up one level to simplify dependencies EE-5726 (#10228) 2023-09-01 13:39:13 -03:00
Prabhat Khera
4560a53317 add tls options to the tls dropdown (#10221) 2023-09-01 10:42:22 +12:00
Chaim Lev-Ari
1b0fd60115 refactor(docker/configs): remove EndpointProvider [EE-5746] (#9198) 2023-08-31 22:11:57 +02:00
Ali
cd3c6e3089 fix(k8sconfigure): make ingress restrict be only [EE-6062] (#10216)
Co-authored-by: testa113 <testa113>
2023-09-01 06:11:48 +12:00
Oscar Zhou
4654978567 fix(api/system): support to display server edition via api (#10211) 2023-08-31 13:39:02 +12:00
Prabhat Khera
6d203033c1 fix showing default ns for ingresses on edi (#10197) 2023-08-29 15:12:49 +12:00
cmeng
4ca45e89c5 fix(relative-path): not deploy git stack via unpacker EE-6043 (#10195) 2023-08-29 11:49:00 +12:00
Prabhat Khera
a8c6bd8082 fix ECR registry token refresh (#10191) 2023-08-29 10:32:41 +12:00
Ali
841ca1ebd4 feat(app): migrate app parent view to react [EE-5361] (#10086)
Co-authored-by: testa113 <testa113>
2023-08-28 09:01:35 +12:00
Chaim Lev-Ari
531f88b947 chore(tests): clean tests output [EE-5758] (#9215) 2023-08-27 12:30:45 +02:00
Dakota Walsh
2953848b9a feat(gpu): remove GPU lightbubble EE-5254 (#10096) 2023-08-25 15:32:08 +12:00
Dakota Walsh
c0ba221021 fix(registry): ecr secret fix [EE-5673] (#10107) 2023-08-25 13:12:41 +12:00
andres-portainer
be85d34c4b fix(logging): enable colored logging EE-5512 (#10097) 2023-08-24 18:40:52 -03:00
cmeng
7125ef81f3 fix(stack): pass registries to unpacker to start stack EE-4797 (#10095) 2023-08-24 13:01:49 +12:00
cmeng
1aae2e27f4 chore(chisel): bump chisel to 1.9.0 EE-5976 (#10093) 2023-08-24 11:06:33 +12:00
cmeng
3237e1990c fix(waiting-room): search endpoints by dynamic edge group name EE-5965 (#10090) 2023-08-24 09:18:59 +12:00
Ali
1e61f7e305 fix(ingress): handle system resources [EE-4775] (#9972)
* fix(ingress): handle system resources [EE-4775]
2023-08-23 09:13:35 +12:00
Chaim Lev-Ari
5586910e9d fix(ui/datatables): sync page count with filtering [EE-5890] (#10010) 2023-08-22 09:36:31 +03:00
Prabhat Khera
bb646162d1 fix wrong error message for secrets (#10073) 2023-08-21 08:05:57 +12:00
Chaim Lev-Ari
cfe0d3092d feat(ui): add confirmation to delete actions [EE-4612] (#10003) 2023-08-19 19:19:02 +03:00
cmeng
6fde4195f8 fix(migrator): prevent duplicated migration EE-5777 (#10077) 2023-08-18 21:40:48 +12:00
Chaim Lev-Ari
36b8c849b3 feat(edge/stacks): reload edge stacks from server [EE-5970] (#10061) 2023-08-17 14:09:41 +03:00
Ali
0f6607e703 refactor(app): migrate the yaml inspector to react [EE-5356] (#10058)
Co-authored-by: testa113 <testa113>
2023-08-17 22:01:10 +12:00
Ali
23295d2736 feat(app): migrate app containers to react [EE-5353] (#9992) 2023-08-17 22:00:25 +12:00
cmeng
6290e9facc fix(waiting-room): search endpoints by edge group name EE-5965 (#10072) 2023-08-17 14:47:09 +12:00
cmeng
95424c322d fix(datatable): image page not loading image list EE-5978 (#10071) 2023-08-17 09:53:28 +12:00
Chaim Lev-Ari
a1e610a39a fix(edge/groups): filter selected environments [EE-5891] (#10050) 2023-08-16 12:24:37 +03:00
Chaim Lev-Ari
a27cc6c0e5 fix(edge/stacks): show pending envs [EE-5913] (#10052) 2023-08-16 10:22:41 +03:00
Ali
2b4cb1b7b4 fix(ingress): empty initial selection + fixes [EE-5852] (#10066)
Co-authored-by: testa113 <testa113>
2023-08-16 18:07:46 +12:00
Ali
26074437ca fix(environments): fix env table [EE-5971] (#10059)
Co-authored-by: testa113 <testa113>
2023-08-16 13:21:23 +12:00
Prabhat Khera
665a25e448 fix edit namespace resource quota issue (#10064) 2023-08-16 10:25:01 +12:00
Chaim Lev-Ari
4a91e947ed feat(edge/configs): add context help [EE-5963] (#10055) 2023-08-15 18:46:58 +03:00
Chaim Lev-Ari
d514eeec86 fix(edge/devices): search waiting room devices [EE-5895] (#10014) 2023-08-15 06:05:10 +03:00
matias-portainer
0ef4aad79a fix(authentication): allow whitespaces when loading AD OU name EE-5206 (#9977) 2023-08-14 12:18:07 -03:00
matias-portainer
8355d449c5 fix(edge/stacks): add pagination to environments list EE-5908 (#10042) 2023-08-14 12:17:00 -03:00
Chaim Lev-Ari
fd7e8a629e feat(edge/stacks): info for old agent status [EE-5792] (#10013) 2023-08-14 16:04:24 +03:00
Ali
7757bf7a84 fix(r2a): remove withUserProvider [EE-5355] (#10048)
Co-authored-by: testa113 <testa113>
2023-08-14 19:01:31 +12:00
Ali
5862aa5dd8 fix(app): use correct withCurrentUser wrapper [EE-5928] (#10040)
Co-authored-by: testa113 <testa113>
2023-08-14 16:53:28 +12:00
cmeng
925a0d0a9a fix(stack): fail to start swarm stack with private image EE-4797 (#10047) 2023-08-14 16:13:12 +12:00
Ali
2a7a96f498 fix(microk8s): PO ui fixes [EE-5900] (#10031)
Co-authored-by: testa113 <testa113>
2023-08-14 12:34:58 +12:00
Ali
c472fe9c18 refactor(app): app events datatable [EE-5355] (#10024) 2023-08-14 05:09:40 +12:00
andres-portainer
0eaf296e1b fix(unpacker): implement unpacker error parsing EE-5779 (#10005) 2023-08-10 10:25:59 -03:00
Oscar Zhou
598b8d0f28 fix(stagger): introduce stack version into DeploymentInfo struct (#10011) 2023-08-10 11:58:40 +12:00
matias-portainer
e1a3010bc7 fix(edge/stacks): fix UI issues EE-5844 (#10021) 2023-08-09 10:09:08 -03:00
cmeng
2de4863532 fix(edge-stack): detaching swarm stack from git repository EE-5812 (#9998) 2023-08-07 10:33:04 +12:00
Oscar Zhou
8cf54cd0df fix(react/datatable): override getColumnCanGlobalFilter method (#9990) 2023-08-07 10:30:38 +12:00
cmeng
1ef1953d7d fix(edge-stack): detaching from git repository EE-5812 (#9989) 2023-08-04 15:17:46 +12:00
cmeng
5b033abaa4 fix(registry): registry login failure for regular stack EE-5832 (#9986) 2023-08-04 15:16:55 +12:00
Ali
5865f1ca77 fix(app): update summary with ingresses [EE-5847] (#9973)
Co-authored-by: testa113 <testa113>
2023-08-04 13:48:21 +12:00
Chaim Lev-Ari
f59573f306 fix(home): empty default sort [EE-5822] (#9951) 2023-08-03 16:21:09 -03:00
Chaim Lev-Ari
1cecbd7177 fix(docker/images): show empty size cell [EE-5823] (#9954) 2023-08-03 16:19:58 -03:00
Ali
acf9203580 fix(ingress): ingress ui feedback [EE-5852] (#9982)
Co-authored-by: testa113 <testa113>
2023-08-03 23:03:09 +12:00
cmeng
9845518aa9 fix(edge-stack): unable to edit edge stack EE-5845 (#9981) 2023-08-03 17:21:01 +12:00
matias-portainer
d7e83aad26 fix(endpoints): fix nil pointer dereference EE-5843 (#9969) 2023-08-02 11:06:34 -03:00
Matt Hook
df47f3d8a8 show kube icon for custom template (#9968) 2023-08-02 09:43:54 +12:00
Ali
d0ecf6c16b fix(ingress): loading and ui fixes [EE-5132] (#9959) 2023-08-01 19:31:35 +12:00
Matt Hook
e400c4dfc6 bump compose to 2.20.2 (#9964) 2023-08-01 12:27:21 +12:00
Matt Hook
721457b71d bump version to 2.20 (#9963) 2023-08-01 09:20:51 +12:00
Ali
b19800681f fix(app): improve perceived ingress load time [EE-5805] (#9946)
Co-authored-by: testa113 <testa113>
2023-07-31 20:18:45 +12:00
cmeng
6a4e44ee0a fix(stack): update gitops updates tooltip EE-5827 (#9962) 2023-07-31 18:46:00 +12:00
Chaim Lev-Ari
37ece734f0 refactor(kube/apps): convert placement table to react [EE-4662] (#8938) 2023-07-29 17:08:41 +02:00
Prabhat Khera
bf79ef7d89 fix(security): upgrade helm binary to v3.12.2 [EE-5801] (#9263) 2023-07-28 15:08:45 +12:00
James Carppe
883ef2578f fix indentation in bug report template (#9944) 2023-07-28 13:05:43 +12:00
Matt Hook
a585f34106 workding change (#9266) 2023-07-28 07:53:33 +12:00
Ali
b128139b69 fix(UI): PO review tweaks [EE-5776] (#9245)
Co-authored-by: testa113 <testa113>
2023-07-28 07:50:53 +12:00
James Carppe
4c425a7af8 Discussions updates (#9730)
* Update bug template: versions to dropdown, add license types to editions, set render on command used

* Update docs URL in help template
2023-07-27 10:27:32 +05:30
Dakota Walsh
400d95c1a5 fix(metrics): node chart race condition EE-5447 (#9249) 2023-07-27 11:46:38 +12:00
Dakota Walsh
ca617e2ac9 fix(jwt): replace deprecated gorilla/securecookie [EE-5153] (#9247) 2023-07-27 09:34:16 +12:00
samdulam
4a90b8a3f7 Fix links in Discussions and Issues Templates (#9258)
* Fix Links

* Fix links for discussions
2023-07-26 12:34:15 +05:30
samdulam
43ad3face2 Fix Links (#9257) 2023-07-26 12:11:06 +05:30
samdulam
69e61be474 file type changes (#9256) 2023-07-26 12:07:06 +05:30
samdulam
a4ea7a3709 Changes to issues templates now that Discussions are enabled (#9255)
* Discussions Enabled and Templates

* Discussions - Ideas Template
2023-07-26 12:02:38 +05:30
samdulam
c5ecf8a66d Change Issues so we can move to discussions 2023-07-26 12:00:41 +05:30
samdulam
c2c0631495 Add Discussion Templates (#9254)
* Discussions Enabled and Templates

* Discussions - Ideas Template
2023-07-26 11:57:59 +05:30
samdulam
4ff3cee72e Add workflow_dispatch so we can run manually (#9253) 2023-07-26 09:33:54 +05:30
Matt Hook
c4e8251e52 post po review changes (#9244) 2023-07-26 11:36:02 +12:00
andres-portainer
21b00c267d fix(docker): use version negotiation for the Docker client EE-5797 (#9250) 2023-07-25 19:00:21 -03:00
samdulam
86ec058347 Change stabot action version as it stopped working (#9246) 2023-07-25 14:47:07 +05:30
Prabhat Khera
9fac997300 warning message placement and persisted folders heading (#9233) 2023-07-24 12:16:58 +12:00
Dakota Walsh
704d70c99b fix(gke): port metrics to the backend EE-5447 (#9041) 2023-07-24 12:16:29 +12:00
Oscar Zhou
e996d29d52 feat(edgestack/async): add Rollbackto field in stackPayload [EE-5684] (#9238) 2023-07-24 10:48:02 +12:00
cmeng
634326b5cd fix(container): column selection UI issue EE-5780 (#9242) 2023-07-24 10:12:43 +12:00
cmeng
94379763f8 fix(edge-stack): edfix(image): force remove button color EE-5787ge stack git authentication issues EE-5595 (#9237) 2023-07-21 16:37:30 +12:00
Ali
bb61723ba1 apply changes from EE (#9232)
Co-authored-by: testa113 <testa113>
2023-07-20 13:48:48 +12:00
cmeng
ff1f549590 fix(edge-stack): edge stack git authentication issues EE-5595 (#9228) 2023-07-20 09:11:38 +12:00
matias-portainer
b8f20a4f05 fix(waiting-room): remove breadcrumb EE-5781 (#9231) 2023-07-19 16:44:37 -03:00
matias-portainer
b5c5df798a chore(edgegroups): rename decoratedEdgeGroup property EE-5501 (#9212) 2023-07-19 12:28:17 -03:00
matias-portainer
88da28694c refactor(docker/events): fix null dataset EE-4667 (#9208) 2023-07-19 12:28:07 -03:00
Chaim Lev-Ari
4f0f53b9aa feat(edge/stacks): ui for status [EE-5593] (#9214) 2023-07-19 12:26:18 -03:00
Chaim Lev-Ari
03b9a9b65d fix(registry): find registry for image [EE-5660] (#9181) 2023-07-19 12:23:38 -03:00
Chaim Lev-Ari
fa755ffbca fix(edge/stacks): show registry field for git [EE-5742] (#9217) 2023-07-19 12:22:29 -03:00
Chaim Lev-Ari
5ad83d0adb fix(endpoints): filter by all edge stack states (#9218) 2023-07-19 12:21:47 -03:00
andres-portainer
9fa097d45f fix(endpointgroups): add transactions support to the User model to avoid a nil pointer dereference EE-5328 (#9221) 2023-07-17 21:23:35 -03:00
cmeng
7acd1080ad fix(edge-stack) make protainer compatible with previous agent EE-5614 (#9220) 2023-07-18 09:25:29 +12:00
andres-portainer
8c533bee67 feat(transactions): migrate some parts to use transactional code EE-5494 (#9213) 2023-07-17 17:36:00 -03:00
Chaim Lev-Ari
fbec123595 style(edge/stacks): remove duplicate component [EE-5554] (#9209) 2023-07-17 09:52:43 +03:00
Chaim Lev-Ari
09f60c3277 feat(docker): migrate files table to react [EE-4663] (#8916) 2023-07-16 10:59:58 +03:00
andres-portainer
146681e1c7 fix(snapshots): increase the chance of taking a snapshot for edge environments EE-4795 (#9211) 2023-07-14 12:34:50 -03:00
Chaim Lev-Ari
615af4fdee refactor(docker/configs): implement table in react [EE-4664] (#8912) 2023-07-14 08:48:08 +03:00
Chaim Lev-Ari
0bcb57568c feat(edge/stacks): increase status transparency [EE-5554] (#9094) 2023-07-13 23:55:52 +03:00
cmeng
db61fb149b feat(edge-stack): per-device-configs-for-edge-stack EE-5461 (#9203) 2023-07-13 15:41:47 -03:00
Chaim Lev-Ari
76b871d8a0 chore(deps): upgrade webpack loaders [EE-5126] (#9206) 2023-07-13 21:36:23 +03:00
Chaim Lev-Ari
a725883cbc refactor(docker/events): convert table to react [EE-4667] (#8937) 2023-07-13 12:55:22 +03:00
Chaim Lev-Ari
ecd54ab929 refactor(docker/images): convert table to react [EE-4668] (#8910) 2023-07-13 10:47:20 +03:00
Chaim Lev-Ari
0e9902fee9 refactor(settings): migrate view to react [EE-5509] (#9179) 2023-07-13 10:46:12 +03:00
cmeng
b93624fa1f fix(chisel): convert seed to private key file EE-5099 (#9149) 2023-07-13 15:19:40 +12:00
Matt Hook
91cfd2d0f2 fix(templates): show correct icon for tempate [EE-5426] (#9202)
* show correct os icon for swarm stacks

* add compose edge stack doc comment
2023-07-13 14:22:53 +12:00
Matt Hook
2d94f020d0 fix react table filter placement (#9200) 2023-07-13 11:47:53 +12:00
James Carppe
01b9c64216 Update 5NF references to 3NF, updated documentation links (#9201) 2023-07-13 11:46:37 +12:00
Oscar Zhou
b93aced176 feat(stack): introduce versioning for stack file [EE-5674] (#9184) 2023-07-13 11:06:24 +12:00
Chaim Lev-Ari
a216a1e960 refactor(edge/stacks): migrate list view to react [EE-2237] (#9186) 2023-07-12 17:26:52 +03:00
Chaim Lev-Ari
020ecb740a chore(ci): run test in github action [EE-3014] (#9187) 2023-07-12 09:27:33 +03:00
Chaim Lev-Ari
de5c959e24 fix(kube): deploy app with image [EE-5738] (#9194) 2023-07-12 09:26:50 +03:00
Dakota Walsh
a9c6fa5ac2 fix(docs): update kubeconfig import doc link EE-5478 (#9193) 2023-07-12 16:07:23 +12:00
Matt Hook
39c431392e indent submenus (#9192) 2023-07-12 15:32:59 +12:00
Matt Hook
cbe23dc753 more robust deletion strategy when removing endpoints (#9191) 2023-07-12 13:20:13 +12:00
Chaim Lev-Ari
afaeddb887 fix(edge/groups): skip count untrusted endpoints [EE-5672] (#9176) 2023-07-11 20:31:32 +03:00
LP B
39eed67fd7 fix(app): dark theme be teaser colors [EE-5621] (#9124)
* fix(app): dark theme be teaser colors

* fix(app): review comments and prettier format

* fix(app): revert changes on arbitrary class

* feat(app/teaser): blue lock svg on dark theme
2023-07-11 17:35:43 +02:00
andres-portainer
64b227b2e1 chore(code): clean up the code EE-5719 (#9183) 2023-07-10 23:26:54 -03:00
Matt Hook
979af5301e correctly identify master nodes (#9064) 2023-07-11 10:22:06 +12:00
Chaim Lev-Ari
10014ae171 refactor(ui/image-config): create react component [EE-5342] (#8856) 2023-07-10 18:56:12 +03:00
andres-portainer
bf51f1b6c9 chore(filenames): fix filenames EE-5717 (#9171) 2023-07-10 12:22:24 -03:00
Chaim Lev-Ari
60ae6a63fc refactor(settings): migrate ssl panel to react [EE-5506] (#9163) 2023-07-10 14:03:58 +03:00
Ali
c752b98120 fix(app): fix app ingress edge cases [EE-5663] (#9150)
Co-authored-by: testa113 <testa113>
2023-07-10 16:20:22 +12:00
Chaim Lev-Ari
8b11e1678e refactor(settings): migrate hidden containers panel to react [EE-5507] (#9119) 2023-07-10 03:39:11 +07:00
andres-portainer
eefb4c4287 fix(polling): clean up the logic for correctness and performance EE-5700 (#9169) 2023-07-07 18:00:20 -03:00
matias-portainer
29c1862754 fix(api): add missing public access middleware to routes EE-5191 (#9086) 2023-07-07 17:37:42 -03:00
Oscar Zhou
801b7d43ee refactor(edgestack): rename version to fileVersion (#9157) 2023-07-07 15:48:45 +12:00
Prabhat Khera
14d84c8025 fix(UI): message styling on ingress form EE-5665 (#9161) 2023-07-07 09:37:04 +12:00
Prabhat Khera
d8c4dcbe72 fix secret warning message styling (#9160) 2023-07-06 15:23:41 +12:00
andres-portainer
31d68f8091 fix(snapshots): avoid a last-write-wins situation EE-5701 (#9156) 2023-07-05 17:27:05 -03:00
andres-portainer
91088a5e0f fix(polling): reorder operations to avoid updating untrusted environments EE-5700 (#9155) 2023-07-05 17:26:52 -03:00
andres-portainer
e4ae4d5312 fix(edgegroups): fix updateEndpointStacks() EE-5699 (#9154) 2023-07-05 14:03:48 -03:00
andres-portainer
74515f102d fix(docker-proxy): reduce DB writes to optimize the proxy calls EE-5516 (#9148) 2023-07-05 09:25:05 -03:00
andres-portainer
b37120802e fix(edgegroups): avoid doing extra operations EE-5627 (#9144) 2023-07-04 16:57:20 -03:00
Chaim Lev-Ari
f5e09618f0 feat(edge): add EnvVar to stack details [EE-5463] (#9036) 2023-07-04 11:14:35 +07:00
Oscar Zhou
1a9a564553 fix/EE-5636/edge-stack-git-update-button-status (#9135) 2023-07-03 10:18:50 +12:00
andres-portainer
8a432ebbf8 fix(edgegroups): remove duplicated endpoints when updating an edge group EE-5679 (#9138) 2023-06-30 18:39:56 -03:00
Oscar Zhou
bc47061624 feat(edgestack): git stack versioning [EE-5458] (#9126) 2023-06-30 16:49:38 +12:00
Chaim Lev-Ari
ceabb2884b fix(edge/stacks): ignore missing file [EE-5649] (#9127) 2023-06-29 13:32:04 +07:00
Chaim Lev-Ari
f293ea41d3 refactor(settings): migrate helm cert panel to react [EE-5505] (#9132) 2023-06-29 13:31:17 +07:00
Chaim Lev-Ari
c452de82b7 refactor(libstack): use libstack [EE-5474] (#9122) 2023-06-28 08:03:52 +07:00
Chaim Lev-Ari
599d214e50 refactor(docker): remove EndpointProvider from commit [EE-5641] (#9123) 2023-06-28 08:02:43 +07:00
Oscar Zhou
f02ede00b3 fix(docker/tls): update tls certs for Docker API env [EE-4286] (#9112) 2023-06-28 08:51:58 +12:00
andres-portainer
f1f46f4da1 fix(boltdb): remove undefined behavior when deleting objects while iterating EE-5643 (#9129) 2023-06-27 16:42:52 -03:00
Oscar Zhou
c96e076871 feat(edge/stack): add stack deployment info struct [EE-5523] (#9042) 2023-06-26 18:12:15 +12:00
Ali
89c1d0e337 feat(app): add ingress to app service form [EE-5569] (#9106) 2023-06-26 16:21:19 +12:00
Chaim Lev-Ari
8c16fbb8aa refactor(libstack): move library to portainer [EE-5474] (#9120) 2023-06-26 08:11:05 +07:00
Chaim Lev-Ari
11571fd6ea refactor(edge/stacks): migrate envs table to react [EE-5613] (#9093) 2023-06-25 12:38:43 +07:00
Chaim Lev-Ari
dfc1a7b1d7 refactor(docker/images): remove EndpointProvider from build [EE-5551] (#9020) 2023-06-25 08:02:54 +07:00
cmeng
7cb6e3f66a feat(edge-stack): relative path support for edge stack EE-5521 (#9103) 2023-06-23 09:41:50 +12:00
andres-portainer
4cc96b4b30 feat(dataservices): unify access methods and abstract away redundant code [EE-5628] (#9115) 2023-06-22 18:28:07 -03:00
cmeng
4c6bbe9a2f fix(registry) undefined error on image tag view EE-4836 (#8885) 2023-06-23 09:07:52 +12:00
Chaim Lev-Ari
ea2f752a4f fix(edge/updates): validate amount of environments [EE-5053] (#9014) 2023-06-22 11:13:57 -03:00
Chaim Lev-Ari
4c8af378af fix(access-control): set user id when private (#8839) 2023-06-22 11:12:49 -03:00
Chaim Lev-Ari
e91b4f5c83 refactor(groups): migrate groups selectors to react [EE-3842] (#8936) 2023-06-22 11:11:10 -03:00
Chaim Lev-Ari
2018529add fix(kube/setup): toggle section on click [EE-4799] (#9107) 2023-06-22 11:10:04 -03:00
Chaim Lev-Ari
58651810bd fix(custom-templates): update template from git [EE-5534] (#9053) 2023-06-22 11:08:47 -03:00
Chaim Lev-Ari
2363d23de0 fix(stacks): fix stop and start [EE-5572] (#9050) 2023-06-22 11:08:24 -03:00
Chaim Lev-Ari
2cd5d55b00 fix(edge/stacks): fix ui issues [EE-5578] (#9070) 2023-06-22 11:08:04 -03:00
Prabhat Khera
3d22cde096 fix fallback rule (#9114) 2023-06-22 09:33:22 +12:00
Oscar Zhou
cd89487c41 fix(environment): blank environments list page [EE-5615] (#9113) 2023-06-22 09:05:58 +12:00
andres-portainer
b12e1aade4 fix(boltdb): remove extra allocation and copy from GetObject() EE-5622 (#9111) 2023-06-20 18:29:33 -03:00
andres-portainer
716c196682 feat(dataservices): abstract away some redundant code EE-5620 (#9110) 2023-06-20 17:51:34 -03:00
Chaim Lev-Ari
7dc6a1559f refactor(settings): kube settings panel [EE-5504] (#9079) 2023-06-20 11:02:39 +07:00
matias-portainer
806e1fdffa fix(environments): fix Add Environments button link EE-5616 (#9105) 2023-06-19 17:56:51 -03:00
LP B
2eca5e05d4 fix(edge-stack): URI too large error for edge stacks with a large amount of environments [EE-5583] (#9085)
* refactor(edge-stacks): filter endpoints by edgeStack

* feat(api/endpoints): edge stack filter support filtering on status in stack

* refactor(endpoints): use separate query params and not JSON query param when querying for an edge stack

* feat(api/endpoints): handle stack filter on dynamic groups + unique list with multiple groups sharing environments

* fix(app/endpoints): edge stack related query params type definition

* fix(api/endpoints): rebase conflicts on imports
2023-06-19 11:55:33 +02:00
Prabhat Khera
223dfe89dd fix(ingress): remove path from ingress host when added EE-5406 (#9099)
* remove path from ingress host when added

* add icon to message
2023-06-19 14:11:50 +12:00
Oscar Zhou
9f9cdf7d43 refactor(settings/backup): migrate backup setting module [EE-5508] (#9076) 2023-06-19 09:57:33 +12:00
Chaim Lev-Ari
caf87bb0b5 refactor(environments): migrate table to react [EE-4702] (#8882) 2023-06-18 12:18:55 +07:00
andres-portainer
f7dd73b0f7 feat(unit-testing): add a mock for the RequestBouncer EE-5610 (#9089) 2023-06-16 10:44:22 -03:00
andres-portainer
933e764a13 fix(endpoints): document an undocumented parameter EE-5556 (#9088) 2023-06-16 10:43:37 -03:00
andres-portainer
e43973da1a fix(logging): skip caller frames to give better context EE-5227 (#9087) 2023-06-15 18:34:11 -03:00
Ali
a2388226ad fix(app): path override validation [EE-5078] (#9077)
Co-authored-by: testa113 <testa113>
2023-06-15 09:05:19 +12:00
Oscar Zhou
0074bcc2ee fix(node): update minimum node version (#9078) 2023-06-14 21:44:12 +12:00
Ali
a4dfeda4ae fix(app): handle no options and volume mounts [EE-5078] (#9075)
* fix(app): handle no options and vol mounts EE-5078

* rm comment

---------

Co-authored-by: testa113 <testa113>
2023-06-14 16:22:44 +12:00
Prabhat Khera
90759182db fix fallback rule and wordings (#9074) 2023-06-14 13:45:25 +12:00
Chaim Lev-Ari
79822e1d3b refactor(edge): move stack response to a shared config [EE-5564] (#9033) 2023-06-13 13:20:02 +07:00
Ali
9d3f13ac92 fix(app): single delete config or secret [EE-5078] (#9069)
Co-authored-by: testa113 <testa113>
2023-06-13 17:03:55 +12:00
Oscar Zhou
2ac70b1eb6 feat(gitops): reword automatic update to gitops update for ui and docs (#9067) 2023-06-13 15:32:10 +12:00
Prabhat Khera
57fa044f2e feat(kubernetes): remove path and service from mandatory fields EE-5406 (#9054) 2023-06-13 12:38:00 +12:00
Ali
3721c1478e fix(app): fix app env var update issues [EE-5078] (#9066)
Co-authored-by: testa113 <testa113>
2023-06-13 09:14:10 +12:00
andres-portainer
424c98e256 fix(http): log HTTP server errors as DEBUG level EE-5225 (#9060) 2023-06-12 09:54:28 -03:00
Ali
2d69e93efa feat(app): rearrange app form services [EE-5566] (#9056) 2023-06-12 11:50:13 +12:00
Ali
d7fc2046d7 feat(config): separate configmaps and secrets [EE-5078] (#9029) 2023-06-12 09:46:48 +12:00
Ali
4a331b71e1 refactor(r2aform): remove validationData [EE-5559] (#9045)
* refactor(r2aform): remove validationData [EE-5559]

* update doc

---------

Co-authored-by: testa113 <testa113>
2023-06-12 08:48:10 +12:00
Chaim Lev-Ari
834ab7c158 fix(docker/images): show image tag [EE-5495] (#9051) 2023-06-11 08:55:19 +07:00
Chaim Lev-Ari
f799dd86c3 chore(deps): upgrade babel [EE-5219] (#9034) 2023-06-11 08:44:20 +07:00
Prabhat Khera
3233987a21 feat(kubernetes): add note teaser and styled application note on details EE-5364 (#9016) 2023-06-09 08:35:29 +12:00
Chaim Lev-Ari
58c1a60fee chore(edge): add test ids [EE-5323] (#9048) 2023-06-08 06:03:04 +07:00
Chaim Lev-Ari
8129e7590b feat(waiting-room): add beta alert to assignment [EE-5384] (#9028) 2023-06-08 06:02:36 +07:00
LP B
73950f3603 fix(app/stacks): swarm stack duplicate and migrate errors [EE-5520] (#9039)
* fix(dev): dev container script

* fix(app/stacks): make swarm stack migrate effectively target the target env and not the current env

* fix(app/stacks): make stack duplicate save the target swarm id on duplicated swarm stack
2023-06-07 14:28:40 +02:00
Chaim Lev-Ari
c7756f3018 refactor(settings): move app settings to panel [EE-5503] (#9043) 2023-06-07 12:16:47 +07:00
cmeng
4f04fe54a7 fix(edge-stack): transmit dot env file to agent [EE-4533] (#8664) 2023-06-06 09:39:08 +12:00
matias-portainer
c90a1be0e5 fix(edgegroups): allow edge groups with no environments or tags EE-4927 (#8439) 2023-06-05 10:18:34 -03:00
Matt Hook
0c5a0eb3a0 fix golint version (#9030) 2023-06-02 17:10:21 +12:00
Prabhat Khera
ecf7f7ec14 update docker go mod to 23.0.3 (#9024) 2023-06-02 11:55:37 +12:00
cmeng
e8e8329aab fix(registry): disable tls for azure [EE-3726] (#8605) 2023-06-02 11:53:46 +12:00
cmeng
4c2906e89d fix(edge) inconsistent heartbeat EE-5533 (#9011) 2023-06-02 10:36:14 +12:00
Matt Hook
fb2646b70c port changes from EE (#9003) 2023-06-02 08:35:15 +12:00
Matt Hook
3cd0409184 fix(build) cleanup build process [EE-5555] (#9026)
* improve makefile and related files

* update wording for build-all target
2023-06-01 13:50:34 +12:00
Matt Hook
1b041a029e increase timeout (#9023) 2023-05-31 18:11:34 +12:00
Ali
69776b4863 refactor(app): app service form to react [EE-5415] (#8994) 2023-05-31 17:58:41 +12:00
Chaim Lev-Ari
2d05103fed refactor(ui): migrate env var field to react [EE-4853] (#8451) 2023-05-31 10:08:41 +07:00
Matt Hook
6b5940e00e add card component (#9022) 2023-05-31 13:18:05 +12:00
Matt Hook
3a49dbf803 add copy to clipboard to web editor (#9009) 2023-05-31 12:28:11 +12:00
Chaim Lev-Ari
1cda08ca11 chore(deps): upgrade css tools [EE-5116] (#8990) 2023-05-31 01:35:25 +07:00
Chaim Lev-Ari
93bf630105 feat(edge/stacks): sync EE codechanges [EE-498] (#8580) 2023-05-31 01:33:22 +07:00
Chaim Lev-Ari
0ec7dfce69 chore(git): ignore go.work.sum [EE-5550] (#9019)
closes [EE-5550]
2023-05-30 21:35:46 +07:00
andres-portainer
eda07614ce chore(unit-test): simplify teardown EE-5536 (#9015) 2023-05-30 11:02:22 -03:00
Chaim Lev-Ari
b498cd657f chore(docs): replace cloudinovasi with portainer [EE-5547] (#9013) 2023-05-30 10:48:21 +07:00
Chaim Lev-Ari
61b568a738 fix(registry): sync config on change [EE-5460] (#8955) 2023-05-30 10:47:44 +07:00
Chaim Lev-Ari
d803d5f821 docs(build): update contrib guidelines to use makefile [EE-5519] (#8998) 2023-05-30 10:46:51 +07:00
Chaim Lev-Ari
2347133438 docs(build): update contrib guidelines to use makefile [EE-5519] (#8997) 2023-05-30 10:46:47 +07:00
Oscar Zhou
96de026eba fix(container/network): recreate container changes static IP [EE-5448] (#8960)
Co-authored-by: Chaim Lev-Ari <chaim.levi-ari@portainer.io>
2023-05-30 09:36:10 +12:00
LP B
d340c4ea96 fix(app/gitform): check if authentication is enabled before using form credentials (#8722) 2023-05-29 18:12:07 +02:00
Chaim Lev-Ari
9567072ce0 chore(deps): upgrade husky and lint-staged [EE-4842] (#8989) 2023-05-29 11:17:42 +07:00
Chaim Lev-Ari
d18b276e30 fix(settings): full width form fields [EE-4954] (#8867) 2023-05-29 10:16:58 +07:00
Ali
af77e33993 refactor(app): details widget migration [EE-5352] (#8886) 2023-05-29 15:06:14 +12:00
cmeng
fdd79cece8 fix(container): delete Mounts field from HostConfig object EE-5387 (#9001) 2023-05-29 09:01:42 +12:00
andres-portainer
ac94d344df fix(customtemplates): set TLSSkipVerify on update EE-5336 (#9007) 2023-05-26 00:29:09 -03:00
andres-portainer
bcbdb01785 fix(kubernetes): fix manifestFilePaths slice creation EE-4554 (#8023) 2023-05-25 11:38:14 -03:00
Chamhaw
a2f734051c fix(service): service related UI issues [EE-4062] (#7943) 2023-05-25 15:59:32 +12:00
cmeng
93866644c6 fix(GPU): EE-4331 error when enabling gpu on existing container (#8316) 2023-05-24 09:21:21 +12:00
Chaim Lev-Ari
6242952141 docs(stacks): require endpoint id [EE-5286] (#8988) 2023-05-23 10:25:32 +07:00
Chaim Lev-Ari
b4dd5c5989 docs(http): sort tags [EE-3697] (#8974) 2023-05-23 10:07:06 +07:00
Chaim Lev-Ari
ef00350922 docs(webhooks): document required endpoint and webhook type [EE-5286] (#8973) 2023-05-23 10:05:55 +07:00
matias-portainer
8acea44ee8 fix(edgejobs): sort logs result in UI EE-5304 (#8746) 2023-05-22 12:25:43 -03:00
Matt Hook
c193360741 cleanup docs (#8949) 2023-05-22 10:50:12 +12:00
Prabhat Khera
4f34a78f7f fix(ui): fix beta alert EE-5498 #8968 2023-05-22 10:17:11 +12:00
Chaim Lev-Ari
f96e7ff434 fix(ui): confirm deletion [EE-4612] (#8868) 2023-05-21 17:16:15 +07:00
Chaim Lev-Ari
e37e87971d fix(stacks): confirm enable tls verification [EE-5410] (#8896) 2023-05-21 12:27:29 +07:00
matias-portainer
5daef54456 fix(stacks): normalize stack name before performing actions EE-4839 (#8539) 2023-05-18 17:58:42 -03:00
andres-portainer
db93e5880f feat(endpointedge): add support for transactions EE-5327 (#8961) 2023-05-18 14:58:33 -03:00
Chaim Lev-Ari
881fa01eb2 fix(docker/networks): load containers from target node [EE-5446] (#8928) 2023-05-18 12:53:34 +07:00
Prabhat Khera
14fa60f6e6 fix(docker): fix extension spelling EE-5277 (#8956) 2023-05-18 10:21:07 +12:00
Prabhat Khera
b58cd1e87e fix(UI): update icons for beta and experimental features EE-5435 (#8940) 2023-05-18 10:19:44 +12:00
andres-portainer
395d86dcd1 feat(settings): add support for transactions EE-5331 (#8957) 2023-05-17 15:00:22 -03:00
andres-portainer
dbd476008b feat(snapshots): add support for transactions EE-5329 (#8947) 2023-05-17 11:57:05 -03:00
LP B
5a04338087 feat(api/stacks): use compose-unpacker to deploy stacks from git [EE-4758] (#8725)
* feat(api/stacks): use compose-unpacker to deploy stacks from git

* refactor(api/stacks): move stack operation as unpacker builder parameter + check builder func existence

* fix(api/stacks): defer removal of unpacker container after error check

* refactor(api/unpacker-builder): clearer code around client creation for standalone and swarm manager

* refactor(api/stacks): extract git stack check to utility function

* fix(api/stacks): apply skip tls when deploying with unpcker - ref EE-5023

* fix(api/stacks): defer close of docker client
2023-05-17 14:52:39 +02:00
Chaim Lev-Ari
dc5f866a24 feat(stacks): add ref to stack.env [EE-5145] (#8872) 2023-05-17 10:30:56 +07:00
Prabhat Khera
83551201fb fix(docker): add docker desktop extension flag in settings and add migration EE-5277 (#8948) 2023-05-17 14:31:46 +12:00
cmeng
e156243e43 fix(code-editor): highlight syntax web editor EE-5405 (#8871) 2023-05-17 14:07:21 +12:00
andres-portainer
1473cc208b feat(edgegroups): add support for transactions EE-5323 (#8946) 2023-05-16 16:07:03 -03:00
andres-portainer
d29b688eb9 feat(endpointgroups): implement support for transactions EE-5328 (#8944) 2023-05-16 14:47:31 -03:00
Chaim Lev-Ari
077046030d chore(deps): upgrade build-tools [EE-5117] (#8577) 2023-05-16 12:22:50 +07:00
Chaim Lev-Ari
5f3c0ff835 fix(ui/form): expandable form section [EE-4799] (#8866) 2023-05-16 10:39:52 +07:00
Chaim Lev-Ari
23e3cdb193 fix(stacks): show containers table [EE-5487] (#8935) 2023-05-16 10:30:34 +07:00
cmeng
e6984c5787 fix(icon) update ecr icon EE-4143 (#8880) 2023-05-16 14:08:23 +12:00
Matt Hook
0743f26ab8 fix(kube): updated kube terminology for configmaps/secrets [EE-4816] (#8770) 2023-05-16 09:21:50 +12:00
Dakota Walsh
8fa49d47f4 fix(docker): search published ports EE-4856 (#8939) 2023-05-15 12:26:42 +12:00
Chaim Lev-Ari
6ef53f0598 chore(deps): upgrade typescript [EE-4841] (#8247) 2023-05-14 16:24:37 +07:00
Chaim Lev-Ari
365316971b feat(waiting-room): choose relations when associated endpoint [EE-5187] (#8720) 2023-05-14 09:26:11 +07:00
andres-portainer
511adabce2 fix(http): drain and close response bodies EE-5486 (#8933) 2023-05-12 17:55:27 -03:00
andres-portainer
5b96136dd2 fix(customtemplates): set TLSSkipVerify properly EE-5336 (#8742) 2023-05-12 09:59:28 -03:00
Ali
42fce1ec57 fix(kube-tables): update table accessor fns [EE-5464] (#8920)
* fix(services): update accessor fns [EE-5464]

* small fixes

---------

Co-authored-by: testa113 <testa113>
2023-05-11 12:55:15 +12:00
Ali
22f4c5d650 separate internal configs with isInternal (#8690)
client-key: /Users/aliharris/.minikube/profiles/minikube/client.key

Co-authored-by: testa113 <testa113>
2023-05-11 08:13:54 +12:00
Prabhat Khera
945798a662 fix(kubernetes): fix light bulb panel for non docker envs EE-5418 2023-05-10 10:43:58 +12:00
Prabhat Khera
6a29198c5c fix note patching for Pod (#8915) 2023-05-10 10:42:56 +12:00
andres-portainer
7197ca435a fix(tls): add missing cipher suites EE-5465 (#8924) 2023-05-09 16:23:27 -03:00
Matt Hook
c3c2221437 fix(docs): fixing missing kube api endpoint docs [EE-5204] (#8843) 2023-05-09 16:42:26 +12:00
Matt Hook
d8fcce4c31 sync makefile with ee (#8918) 2023-05-09 15:19:38 +12:00
Chaim Lev-Ari
c86b76261a fix(gitops): make polling mechanism static button [EE-5420] (#8893) 2023-05-09 08:00:14 +07:00
Chaim Lev-Ari
acc340b324 fix(ui/code-editor): disable multi select [EE-5383] (#8861) 2023-05-09 07:59:34 +07:00
Chaim Lev-Ari
e0609e3d93 docs(teams): fix swagger [EE-5414] (#8890) 2023-05-08 16:00:00 +07:00
Prabhat Khera
926ca19a1b feat(UI): migrate console view to react EE-2276 (#8767) 2023-05-08 14:07:46 +12:00
hungdoo
c03b2ebbc1 wrap response rewrite operation with validation check (#7727) 2023-05-07 13:52:03 +12:00
andres-portainer
e82c88317e feat(edgestacks): add support for transactions EE-5326 (#8908) 2023-05-05 20:39:22 -03:00
cmeng
59f543f442 fix(web-editor) update web editor button color EE-5404 (#8892) 2023-05-05 16:49:11 +12:00
Matt Hook
f092b85f55 feat(makefile): improvements to the makefile and use gotestsum [EE-5439] (#8906)
* makefile improvements. use gotestsum

* increase timeout
2023-05-05 14:35:32 +12:00
Chaim Lev-Ari
cfed481d6e feat(license): remove untrusted devices from node count [EE-5357] (#8817) 2023-05-05 09:02:31 +07:00
Chaim Lev-Ari
5f6ddc2fad fix(edge/stacks): validate deployment type [EE-4580] (#8875) 2023-05-05 09:01:43 +07:00
Matt Hook
334eee0c8c fix(errors): wrap db errors, improve error handling (#8859)
* use error check func, wrap db object not found

* add errorlint and fix all the linting errors

* add exportloopref linter and fix errors

* fix incorrect error details returned on an api

* fix new errors

* increase linter timeout

* increase timeout to 10minutes

* increase timeout to 10minutes

* rebase and fix new lint errors

* make CE match EE

* fix govet issue
2023-05-05 12:19:47 +12:00
Oscar Zhou
550e235d59 fix(admin): infinite loop when initializaing admin user (#8905) 2023-05-05 11:45:03 +12:00
Matt Hook
9970fb3940 fix git options for kube (#8889) 2023-05-05 09:20:20 +12:00
pibica
5d2723f4b9 #8546 fix(logging): manage time in seconds or milliseconds (#8547) 2023-05-05 07:41:11 +12:00
andres-portainer
a062a0bfbe feat(resourcecontrol): add support for transactions EE-5431 (#8901) 2023-05-04 13:24:04 -03:00
andres-portainer
706d66a76e feat(teammemberships): add support for transactions EE-5412 (#8900) 2023-05-04 11:51:30 -03:00
Chaim Lev-Ari
2d22c4ff7d docs(stacks): require endpointId for delete [EE-4334] (#8897) 2023-05-04 21:32:46 +07:00
Chaim Lev-Ari
d77a0887a7 docs(endpoints): deprecate EdgeCheckinInterval [EE-5281] (#8864) 2023-05-04 21:31:57 +07:00
Chaim Lev-Ari
2383d243d5 docs(custom-templates): add missing parameters [EE-5233] (#8865) 2023-05-04 21:31:06 +07:00
Chaim Lev-Ari
426c132f97 refactor(edge/stacks): separate create by method [EE-4947] (#8898) 2023-05-04 21:11:19 +07:00
LP B
1ff19f8604 fix(app/home): env tile hover style [EE-5299] (#8765)
* fix(app/home): environment item hover

* fix(app/home): remove white border above env list footer

* fix(app/home): icon color on edit buttons hover in high contrast theme
2023-05-04 16:00:56 +02:00
Ali
14a581e86b fix(dialog): dialog migration issues [EE-5385] (#8849)
* fix(dialog): dialog migration issues [EE-5385]

* don't highlight slider tooltip text

---------

Co-authored-by: testa113 <testa113>
2023-05-04 16:23:27 +12:00
Oscar Zhou
ed279ba65b fix(edgestack): incorrect response code (#8873) 2023-05-04 10:01:33 +12:00
Oscar Zhou
19eceaf37f fix(restore/swarm): init primary endpoint after admin user is created (#8854) 2023-05-04 09:44:11 +12:00
Oscar Zhou
1963d064a3 fix(swarm/ui): keep stack detail page on the top [EE-4931] (#8858) 2023-05-04 09:29:47 +12:00
Matt Hook
58d130ee37 fix(buildscripts): make build process more closely resemble EE (#8881) 2023-05-03 12:13:28 +07:00
Ali
98e6393274 refactor(app): summary widget migration [EE-5351] (#8796)
* refactor(app): summary widget migration [EE-5351]

* update converter and limit display

---------

Co-authored-by: testa113 <testa113>
2023-05-03 15:55:25 +12:00
andres-portainer
745bbb7d79 feat(roles): add transactions support EE-5390 (#8878) 2023-05-02 19:05:18 -03:00
Chaim Lev-Ari
757461d58b chore(deps): upgrade react-table to v8 [EE-4837] (#8245) 2023-05-02 13:42:16 +07:00
Chaim Lev-Ari
f20d3e72b9 chore(build): remove grunt and add makefile [EE-4824] (#8125) 2023-05-02 10:13:37 +07:00
Prabhat Khera
731f3959c7 fix(UI): update application deploy/update messages EE-4005 (#8819) 2023-05-01 09:14:30 +12:00
cmeng
0f9a0e25f2 fix(login) hide password in console EE-5279 (#8774) 2023-04-29 07:24:33 +12:00
cmeng
ae339a0047 fix(stack) add skip TLS toggle for edit stack EE-5391 (#8851) 2023-04-28 13:35:33 +12:00
Chaim Lev-Ari
77f8b9333a refactor(stacks): break swagger docs by type [EE-5381] (#8820) 2023-04-27 11:03:55 +07:00
Chaim Lev-Ari
bbea0bc8a5 feat(edge): hide envs from waiting room [EE-5185] (#8688) 2023-04-27 09:23:10 +07:00
Chaim Lev-Ari
4b9c857d85 feat(waiting-room): show and filter by check in [EE-5186] (#8701) 2023-04-27 09:22:05 +07:00
Dakota Walsh
b5771df6a8 fix(image): allow dot in image names [EE-4595] (#8619) 2023-04-27 09:44:08 +12:00
matias-portainer
7ed8e9e167 fix(images): avoid returning null on registryId default value EE-5394 (#8841) 2023-04-26 10:24:45 -03:00
Prabhat Khera
80a3a5f16e feat(kubernetes): fix annotation validation EE-5021 (#8818) 2023-04-26 16:48:55 +12:00
Ali
3e654ff9b2 fix(deploy): return to referring view [EE-5345] (#8763)
* fix(deploy): return to referring view [EE-5345]

* no-underline -> no-decoration

---------

Co-authored-by: testa113 <testa113>
2023-04-26 11:23:15 +12:00
LP B
9b287f3020 fix(api/registry): encode X-Registry-Auth header using base64url instead of base64 [EE-4726] (#8492) 2023-04-24 13:57:39 +02:00
Oscar Zhou
a7404e00d1 fix(ci/security): intepret matrix summary as string not shell command (#8836) 2023-04-24 13:21:35 +12:00
Dakota Walsh
3654109332 fix(slider): update rc-slider [EE-5011] (#8611)
* fix(slider): update rc-slider [EE-5011]

* fix PasswordLengthSlider tooltip

* fix unnecessarily bulky className for SliderTooltip

* remove SliderTooltip inner div

* center slider handle value

* relative tooltip

* update z index

---------

Co-authored-by: testa113 <testa113>
2023-04-21 16:52:05 +12:00
Oscar Zhou
bf9dc8c2d0 feat(ci/security): remove deprecated github action command alert [EE-3059] (#8795) 2023-04-21 10:57:38 +12:00
cmeng
67f8e8f3c2 fix(webhook) remove NaN fom webhook url EE-5373 (#8816) 2023-04-21 10:56:53 +12:00
andres-portainer
56d6dfe02e feat(transactions): add transaction support for Registries EE-5382 (#8825) 2023-04-20 18:42:52 -03:00
Ali
861a9a5bbb fix(templates): update name validation [EE-5344] (#8760)
Co-authored-by: testa113 <testa113>
2023-04-21 09:39:55 +12:00
Matt Hook
1b470845b8 better logging during critical migration error (#8576) 2023-04-21 09:30:12 +12:00
Matt Hook
3c26aa8f34 feat(packages): upgrade packages [EE-5147] (#8658)
* upgrade packages

* update eksctl to match ee

* update helm to latest
2023-04-20 13:31:29 +12:00
Ali
de953da5a4 fix(editor): fix styles [EE-5369] (#8809)
* fix(editor): fix styles [EE-5369]

* rm hash

---------

Co-authored-by: testa113 <testa113>
2023-04-20 08:27:25 +12:00
Chaim Lev-Ari
5356d1feeb fix(edge/updates): add padding for edge groups [EE-5349] (#8772) 2023-04-18 13:40:12 +12:00
Matt Hook
7a8a20e0cc feat(libhelm): allow passing optional env and http client [EE-5252] (#8758) 2023-04-14 14:50:37 +12:00
andres-portainer
a7474188b9 chore(deps): unify dependencies EE-5360 (#8778) 2023-04-13 18:07:32 -03:00
cmeng
6fe56f89c6 fix(backup) add description text to backup EE-5283 (#8775) 2023-04-13 16:05:12 +12:00
Oscar Zhou
a98f480974 fix(swagger): correct endpoint api annotations [EE-5333] (#8761) 2023-04-13 15:31:27 +12:00
cmeng
8ccac7c98f fix(stack): upgrade docker-compose EE-5334 (#8757) 2023-04-11 17:56:00 +12:00
andres-portainer
e0ce3671e8 fix(tags): migrate to transactional code EE-5330 (#8755) 2023-04-10 19:03:51 -03:00
andres-portainer
62128d1069 fix(edgejobs): migrate to transactional code EE-5324 (#8747) 2023-04-10 15:59:34 -03:00
Oscar Zhou
a65ffe519a fix(k8s/gitops): missing git auth toggle in k8s app edit page [EE-5320] (#8741) 2023-04-10 20:14:13 +12:00
Ali
5ac1ea3df8 fix(ns): add selection caching back [EE-5273] (#8738)
Co-authored-by: testa113 <testa113>
2023-04-06 14:28:01 +12:00
Matt Hook
bf56bdb8f6 search for correct source directory when doing a restore (#8676) 2023-04-06 10:39:10 +12:00
cmeng
b00aa68c2b fix(homepage) move heartbeat logic to backend EE-5317 (#8737) 2023-04-06 09:09:22 +12:00
Matt Hook
8c5edd2c97 fix(docs): add missing swagger docs for upload file [EE-4886] (#8708)
* add docs for uploading files via host management features

* fix other doc issues
2023-04-04 16:59:34 +12:00
Oscar Zhou
c650868fe9 feat(templates): allow managing git based templates [EE-2600] (#7855)
Co-authored-by: itsconquest <william.conquest@portainer.io>
Co-authored-by: oscarzhou <oscar.zhou@portainer.io>
Co-authored-by: Chaim Lev-Ari <chiptus@users.noreply.github.com>
2023-04-04 12:44:42 +12:00
cmeng
30a2bb0495 fix(security): potential vulnerability of path traversal attacks EE-5303 (#8728) 2023-04-04 09:00:17 +12:00
andres-portainer
1a451823d9 fix(edgestacks): fix a deadlock in UpdateEdgeStackFunc() (#8735) 2023-04-03 14:24:27 -03:00
Chaim Lev-Ari
feab2a757e feat(gitops): allow to skip tls verification [EE-5023] (#8668) 2023-04-03 09:19:17 +03:00
andres-portainer
17839aa473 fix(endpointrelation): change a callback so it is transactional EE-5312 (#8729) 2023-03-30 23:16:56 -03:00
Prabhat Khera
fc1aec3bb8 fix(ui): namespace caching issue EE-5273 (#8709)
* fix namespace caching issue

* fix(apps): add loading state [EE-5273]

* rm endpoint provider

* fix(namespace): remove caching [EE-5273]

* variable typo

---------

Co-authored-by: testa113 <testa113>
2023-03-31 13:24:57 +13:00
Chaim Lev-Ari
d64e7eacfc fix(ui/code-editor): stretch code editor content full height [EE-5202] (#8673) 2023-03-30 12:26:32 +03:00
Ali
7f805ac5be fix(ns): save filter to local storage [EE-5287] (#8723)
* fix(ns): save filter to local storage [EE-5287]

* allow system ns and save per user

---------

Co-authored-by: testa113 <testa113>
2023-03-30 11:21:05 +13:00
Chaim Lev-Ari
308a78db21 refactor(edge): deprecate IsEdgeDevice [EE-5046] (#8534) 2023-03-28 09:19:22 +03:00
andres-portainer
814fc9dfc0 fix(http): drain and close HTTP response bodies EE-5280 (#8716) 2023-03-27 15:14:16 -03:00
andres-portainer
3635df89dc fix(snapshots): change the snapshot object to maintain backwards compatibility EE-5240 (#8705) 2023-03-23 13:30:32 -03:00
Ali
30248eabb4 fix(apps) UI release fixes [EE-5197] (#8702)
* fix(apps) searchbar flex resizing and insights

* UI fixes

* update stacks datatable

---------

Co-authored-by: testa113 <testa113>
2023-03-23 08:20:30 +13:00
Ali
3636ac5c26 fix(dashboard): use faster proxy request [EE-5160] (#8693)
Co-authored-by: testa113 <testa113>
2023-03-22 15:34:44 +13:00
Prabhat Khera
f6e8b25cf3 fix Gpus null issue (#8692) 2023-03-21 16:06:01 +13:00
Oscar Zhou
124e0bf9b9 fix(stack/git): unexpected cursor movement in git text fields [EE-5143] (#8655) 2023-03-20 10:00:49 +13:00
Chaim Lev-Ari
45def82156 fix(ui/box-selector): BE link and use icons standard size [EE-5133] (#8607) 2023-03-19 13:37:35 +01:00
andres-portainer
76bdf6f220 fix(websocket): use the read part of the buffer instead of everything EE-5235 (#8685) 2023-03-17 17:23:24 -03:00
Ali
e142be399d fix(kubeconfig): fix download checkbox [EE-5199] (#8674)
Co-authored-by: testa113 <testa113>
2023-03-17 10:33:56 +13:00
Ali
13ba72ee07 fix(wizard): Capitalise Kubernetes [EE-5178] (#8662)
Co-authored-by: testa113 <testa113>
2023-03-16 18:50:54 +13:00
Dakota Walsh
f17a608dc7 fix(kubernetes): Prevent rerunning initial cluster detection [EE-5170] (#8666) 2023-03-16 15:39:26 +13:00
Prabhat Khera
6ee5cc6a56 fix(ui): namespace cache refresh on reload EE-5155 (#8644) 2023-03-16 10:10:37 +13:00
andres-portainer
44582732bb fix(home): exclude snapshots from the home page to improve the loading times EE-5154 (#8626) 2023-03-15 15:16:41 -03:00
andres-portainer
ea03024fbc fix(edgegroup): fix data race in edge group update EE-4441 (#8523) 2023-03-15 14:53:38 -03:00
Oscar Zhou
795e6a5b3c fix(stack/git): unable to move git repository error [EE-5144] (#8618) 2023-03-15 12:54:09 +13:00
andres-portainer
2b17cb9104 fix(kubernetes): fix data-race in GetKubeClient() EE-4436 (#8498) 2023-03-14 20:11:28 -03:00
andres-portainer
347f66b1f1 fix(edge): fix status inspect error message EE-5190 (#8661) 2023-03-14 13:28:20 -03:00
Ali
40c387f4f4 fix(annotations) ingress tip to match ee [EE-5158] (#8653)
Co-authored-by: testa113 <testa113>
2023-03-14 10:41:35 +13:00
andres-portainer
15cbdb8af9 chore(portainer): clean up the code EE-5188 (#8660) 2023-03-13 13:18:28 -03:00
matias-portainer
621a01ba3b fix(upgrade): remove yellow upgrade banner EE-5141 (#8640) 2023-03-13 09:01:27 -03:00
Ali
37f382d286 fix(kube): check for ns on enter [EE-5160] (#8647)
Co-authored-by: testa113 <testa113>
2023-03-13 13:57:11 +13:00
Prabhat Khera
77b49ae9c5 fix typo in delete image modal dialog (#8621) 2023-03-13 11:05:51 +13:00
Matt Hook
29648f517b reduce throttling in the kube client (#8630) 2023-03-13 09:44:27 +13:00
Ali
8f42af49e8 fix(annotation): update wording/styling [EE-5158] (#8642)
Co-authored-by: testa113 <testa113>
2023-03-10 16:52:09 +13:00
cmeng
0ab7987684 fix(edge-stack) always show edge group selector [EE-5157] (#8639) 2023-03-10 10:48:44 +13:00
Ali
31d956dbcb fix(app): restrict ns fix create app [EE-5123] (#8597)
* fix(app): restrict ns fix create app [EE-5123]

* fix node limits race condition

---------

Co-authored-by: testa113 <testa113>
2023-03-10 10:24:23 +13:00
Ali
2cc80e5e5d refactor(GPU): refactor to colocate and simplify UI work [EE-5127] (#8593)
* refactor to colocate and simplify

* fix(insights): text size to match portainer views

---------

Co-authored-by: testa113 <testa113>
2023-03-09 22:06:57 +13:00
matias-portainer
fb6e26a302 fix(stacks): pass WorkingDir to deployer command EE-5142 (#8615) 2023-03-08 19:34:57 -03:00
Matt Hook
9cca299833 fix(gotest): fix go tests as part of version bump to 2.19.0 (#8623)
* bump version to 2.19

* fix broken go tests
2023-03-08 17:23:34 +13:00
Chaim Lev-Ari
4c86be725d feat(system): upgrade portainer on kubernetes [EE-4625] (#8448) 2023-03-07 23:34:55 -03:00
Chaim Lev-Ari
0669ad77d3 fix(home): disable live connect for async [EE-5000] (#8462) 2023-03-07 21:27:34 -03:00
Matt Hook
2bfc956f58 bump version to 2.19 (#8617) 2023-03-08 13:24:59 +13:00
Ali
89194405ee fix(kube): improve dashboard load speed [EE-4941] (#8572)
* apply changes from EE

* clear query cache when logging out

* Text transitions in smoother
2023-03-08 11:22:08 +13:00
Chaim Lev-Ari
5f0af62521 fix(git): fix more files form error [EE-5125] (#8601) 2023-03-07 18:44:26 -03:00
Dakota Walsh
e3299eddd5 fix(docker): container health table [EE-5008] (#8591) 2023-03-08 10:33:15 +13:00
Prabhat Khera
bdde278139 feat(applications): application page performance improvements EE-4956 (#8569) 2023-03-08 10:27:42 +13:00
Chaim Lev-Ari
01ea9afe33 feat(edge): hide FDO features behind a flag [EE-5128] (#8600) 2023-03-07 13:45:39 -03:00
matias-portainer
8345d1471e fix(stacks): remove old dependency from stacks redeploy controller EE-5138 (#8609) 2023-03-07 13:35:43 -03:00
Prabhat Khera
2a55d20eff fix(annotations): minor issues in Annotations EE-4089 (#8612) 2023-03-07 21:09:41 +13:00
Ali
7dca784ec6 fix(env): get edited environment info [EE-5129] (#8603)
Co-authored-by: testa113 <testa113>
2023-03-07 14:44:19 +13:00
Oscar Zhou
37484566eb fix(ui/wizard): edge agent ui issue [EE-5126] (#8598) 2023-03-07 10:27:40 +13:00
Chaim Lev-Ari
70710cfeb7 feat(edge): associate edge env to meta fields [EE-3209] (#8551)
* refactor(edge/groups): load edge groups in selector

fix(edge/stacks): remove double groups title

* feat(edge): supply meta fields to edge script [EE-5043]

* feat(edge): auto assign aeec envs to groups and tags [EE-5043]

fix [EE-5043]

fix(envs): fix global key test

* fix(edge/groups): save group type

* refactor(edge/devices): move loading of devices to table

* refactor(tags): select paramter for query

* feat(edge/devices): show meta fields

* refactor(home): simplify filter

* feat(edge/devices): filter by meta fields

* refactor(edge/devices): break filter and loading hook
2023-03-07 09:25:04 +13:00
Chaim Lev-Ari
03712966e4 fix(ui/code-editor): apply theme colors [EE-5104] (#8558)
* fix(ui/code-editor): apply theme colors [EE-5104]

fix [EE-5104]

* fix(kube/yaml): expand yaml inspector

* fix(ui/code-editor): have default height

* fix(ui/code-editor): boolean color

* fix(ui/code-editor): style search bar
2023-03-06 09:13:42 +02:00
Chaim Lev-Ari
07100258cd fix(git): file path background [EE-5114] (#8573)
* fix(git): file path background [EE-5114]

also disabled url check on CE because
the http handler wasn't available and raised errors

* fix(git): highlight hovered path option

* feat(git): hide path options when choosing
2023-03-06 09:13:22 +02:00
cmeng
4c6f5f961e fix(wizard): fix tunnel add validation [EE-5124] (#8595) 2023-03-06 10:43:08 +13:00
Chaim Lev-Ari
77e1f5aa34 fix(oauth): danger confirm for hiding prompt [EE-4576] (#8574) 2023-03-05 15:22:49 +02:00
Chaim Lev-Ari
3baab6d695 fix(ui): import components for envs and registries [EE-5120] (#8583) 2023-03-05 15:22:00 +02:00
Chaim Lev-Ari
d546ff269b fix(ui/input-group): remove z-index [EE-5121] (#8582) 2023-03-05 12:14:08 +02:00
cmeng
60275dd31c feat(edge): EE-4621 support high latency for tunnel (#8302) 2023-03-04 09:13:37 +13:00
Ali
07df4b1591 fix(notifications): limit only in header [EE-4815] (#8579)
Co-authored-by: testa113 <testa113>
2023-03-03 15:08:15 +13:00
Ali
fd916bc8a2 feat(gpu): rework docker GPU for UI performance [EE-4918] (#8518) 2023-03-03 14:47:10 +13:00
Ali
769c8372fb feat(microk8s): BE teasers in wizard [EE-4772] (#8449)
* feat(microk8s): separate existing/new envs EE-4772

update icons

fix be teaser box selector style

* match environment wizard box selectors

* revert title back

* updated kaas description in wizard

---------

Co-authored-by: testa113 <testa113>
Co-authored-by: Prabhat Khera <prabhat.khera@portainer.io>
2023-03-03 12:27:24 +13:00
James Carppe
d032119ebc Update docs URL and add --log-level docs (#8511) 2023-03-03 11:38:19 +13:00
Matt Hook
ac47649631 feat(kubernetes): list all kube services screen [EE-1571] (#8524)
* port services from ee

* fix external link

* post review improvements

* remove applications-ports-datatable

* minor post review updates

* add services help url

* post review update

* more post review updates

* post review updates

* rename index to component

* fix external ip display and sorting

* fix external apps tag

* fix ingress screen time format

* use uid for row id. Prevent blank link

* fix some missing bits ported from EE

* match ee

* fix display of show system resources

* remove icon next to service type
2023-03-03 08:45:19 +13:00
Chaim Lev-Ari
8d6797dc9f fix(edge): show edge settings [EE-4959] (#8581)
* fix(edge): show edge settings [EE-4959]

fix [EE-4959]

* fix(edge/settings): validate ce
2023-03-02 19:32:43 +02:00
andres-portainer
197b0bcbde fix(code): add missing returns after sending HTTP errors EE-4442 (#7868) 2023-03-02 12:52:10 -03:00
Chaim Lev-Ari
6918da2414 refactor(stacks): extract auto update logic [EE-4945] (#8545) 2023-03-02 17:07:50 +02:00
cmeng
085381e6fc fix(logs-viewer): fail to search json logs [EE-4857] (#8482) 2023-03-02 23:55:13 +13:00
Chaim Lev-Ari
6074d1fcb5 fix(docker): clone config [EE-5115] (#8575) 2023-03-02 11:38:21 +02:00
Ali
96e5d44cc2 fix(teaser): add up-to-date teaser [EE-5014] (#8548)
Co-authored-by: testa113 <testa113>
2023-03-02 10:14:50 +13:00
matias-portainer
a45ef3d72e fix(azure): ensure azure client HTTPS verification EE-4444 (#8137) 2023-03-01 17:52:29 -03:00
Chaim Lev-Ari
c819d4e7f7 feat(environments): create async edge [EE-4480] (#8527) 2023-03-01 20:33:05 +02:00
LP B
bc6a667a6b feat(api/snapshot): extend docker container snapshot type (#8537) 2023-03-01 17:33:40 +01:00
matias-portainer
7dcd6f9b9e fix(ui): fix search in associated endpoints selector EE-4532 (#8454) 2023-03-01 10:34:07 -03:00
Chaim Lev-Ari
c8d334e603 fix(server): skip file deletion errors [EE-4916] (#8443) 2023-03-01 13:44:34 +02:00
Chaim Lev-Ari
ab9b0c2147 feat(docker/containers): show name in log view [EE-5108] (#8557) 2023-03-01 09:37:23 +02:00
cmeng
6d659b4a2c fix(edge-stack): add validation for edge stack name [EE-4283] (#8504) 2023-03-01 19:51:08 +13:00
Prabhat Khera
defce0cf6d feat(kuberenetes): add annotations to kube objects EE-4089 (#8499)
* add annotations BE teaser
* fix settings icon click on home screen for kube env
* add debouce to namespace validation
* ingress button tooltip fixed
* fix tooltip text
2023-03-01 13:11:12 +13:00
Dakota Walsh
5f66020e42 fix(docker): container health alignment EE-5008 (#8553) 2023-03-01 12:48:41 +13:00
Chaim Lev-Ari
b3e72ecaa0 fix(gitops): load correct auth creds [EE-4849] (#8550)
close [EE-4849]
2023-02-28 17:55:54 +02:00
Chaim Lev-Ari
b98c71f1ab refactor(ui): move react components to react codebase [EE-3354] (#8258)
* refactor(ui): move react components to react codebase [EE-3354]

* refactor(app): move bocx selector options

* refactor(react): spearate portainer components

* fix(app): fix imports
2023-02-28 17:32:29 +02:00
matias-portainer
f9a09301a8 fix(edgejobs): fix data race in edge jobs tasks collect EE-4766 (#8542) 2023-02-28 12:14:09 -03:00
Chaim Lev-Ari
2c247efd0f fix(settings/oauth): show internal auth prompt by default [EE-4576] (#8481)
* fix(settings/oauth): show internal auth prompt by default [EE-4576]

fix [EE-4576]

* fix(oauth): use new confirm modal
2023-02-28 16:49:46 +02:00
Chaim Lev-Ari
86d0e30eb7 fix(build): ignore source maps for 3rd party [EE-5106] (#8549) 2023-02-28 14:54:52 +02:00
Chaim Lev-Ari
69a91ff90a fix(ui/box-selectors): make multi use square [EE-3856] (#8470) 2023-02-27 12:47:29 +02:00
Chaim Lev-Ari
e0481f69b1 chore(deps): replace semver compare with local solutions [EE-5018] (#8491) 2023-02-26 15:25:24 +02:00
matias-portainer
088262b6dc fix(edgejobs): fix data race on task logs clear EE-4767 (#8236) 2023-02-23 16:21:17 -03:00
Oscar Zhou
1b12ee9f01 fix(security): update dependency and binary version [EE-4863] (#8394) 2023-02-23 22:29:48 +13:00
Chaim Lev-Ari
5507b1e8c9 refactor(ui/editor): migrate code-editor to react [EE-4848] (#8257) 2023-02-23 09:10:31 +04:00
Chaim Lev-Ari
273a3f9a10 refactor(gitops): migrate git form to react [EE-4849] (#8268) 2023-02-23 01:43:33 +05:30
Oscar Zhou
afe6cd6df0 fix(code/security): vcs status error [EE-5062] (#8510) 2023-02-22 10:40:09 +13:00
andres-portainer
95ac2cc4c3 chore(edge): add transaction support for common objects EE-5079 (#8522) 2023-02-20 16:11:18 -03:00
Chaim Lev-Ari
9a8e95d017 feat(upgrade): show subtle banner [EE-5017] (#8489) 2023-02-19 09:47:50 +05:30
Oscar Zhou
631503fc1b fix(upgrade): add label to upgrade mustache template [EE-5029] (#8505) 2023-02-17 13:56:01 +13:00
Ali
23f3008500 chore(notifications):improve performance [EE-4815] (#8475)
* chore(notifications):improve performance [EE-4815]

Co-authored-by: testa113 <testa113>
2023-02-17 12:51:00 +13:00
matias-portainer
89dd72b4ac fix(registry): correct words in registry page and other places EE-4894 (#8356) 2023-02-16 10:31:08 -03:00
Chaim Lev-Ari
5a375ff055 refactor(environment/edge): show copy token button [EE-3691] (#8497) 2023-02-16 09:13:30 +05:30
andres-portainer
f081631808 fix(edgegroups): fix data-race in edgeGroupCreate EE-4435 (#8477) 2023-02-14 15:18:07 -03:00
Chaim Lev-Ari
e66dea44e3 refactor(ui/modals): replace bootbox with react solution [EE-4541] (#8010) 2023-02-14 13:49:41 +05:30
cmeng
392c7f74b8 fix(backup): reword region placeholder [EE-5012 (#8496) 2023-02-14 16:13:53 +13:00
Matt Hook
1dba5e464b minor fix (#8485) 2023-02-14 10:35:21 +13:00
Matt Hook
56dc2d1000 fix typo (#8486) 2023-02-13 23:23:44 +13:00
Ali
5c05ec489e fix(nameField): imperatively call setDebouncedValue when the value is changed by setFieldValue [EE-5002] (#8468)
Co-authored-by: testa113 <testa113>
2023-02-13 15:06:14 +13:00
Oscar Zhou
cef9255161 fix(snapshot): prevent snapshot containers from fast failing in Swarm mode (#8308) 2023-02-13 14:42:10 +13:00
Ali
0befdacc0e chore(prettier): ignore tailwind changes [EE-4809] (#8483)
Co-authored-by: testa113 <testa113>
2023-02-13 11:38:06 +13:00
Matt Hook
b2105f3614 feat(dockerfile): layered images [EE-4879] (#8301)
* multistage build to target production and storybook

* missing slash

* add storybook for windows too and build targets

* feature flag the storybook api

* remove kompose and prep for new FF lib

* todo comment for FF

* update to new feature flags library

* simplify logic

* fix compilation error

* simplified it
2023-02-13 11:28:32 +13:00
Ali
58d66d3142 chore(prettier): add tailwind prettier plugin [EE-4809] (#8221)
* add prettier plugin

* apply tailwind prettier formatting
2023-02-13 10:04:24 +13:00
Ali
9f6702d0b8 feat(resourcequotas): reduce resource quota requests [EE-4757] (#8420) 2023-02-10 18:28:53 +13:00
Matt Hook
44d69f3a3f hide the password in the response (#8437) 2023-02-10 18:26:18 +13:00
Matt Hook
e255bd710f chore(golang) remove api pkg and go mod tidy (#8474)
* remove this.  It exists and should be in the top level directory

* go mod tidy
2023-02-10 16:48:53 +13:00
Prabhat Khera
d73622ed9c fix cursor on autofill (#8378) 2023-02-10 09:19:43 +13:00
andres-portainer
4753d52532 fix(tls): specify the TLS MinVersion always EE-4427 (#7869) 2023-02-09 16:13:35 -03:00
Matt Hook
f9bbe000fb fix(docker): remove prepended slash by default on container names [EE-3592] (#8195)
* remove prepended slash by default if present

* trimcontainername still needed
2023-02-09 17:18:19 +13:00
Matt Hook
bfc610c192 feat(featureflags): improved feature flag handling [EE-4609] (#8222)
* updated and improved feature flags using new module

* merge init into parse

* update the package documentation

* better docs

* minor tidy
2023-02-09 17:17:46 +13:00
cmeng
51b9804fab fix(updater): specify docker client version [EE-5010] (#8459) 2023-02-08 17:00:22 +13:00
Matt Hook
e2168d21c7 guard around kube actions for endpoint inspect (#8430) 2023-02-07 23:13:52 +13:00
Chaim Lev-Ari
2dddc1c6b9 refactor(ui/box-selector): replace all selectors [EE-3856] (#7902) 2023-02-07 09:03:57 +05:30
Ali
c9253319d9 fix(texttip): fix texttip in edge groups [EE-4990] (#8441)
Co-authored-by: testa113 <testa113>
2023-02-03 13:39:45 +13:00
Chaim Lev-Ari
968fc98401 fix(home): show tooltip for disabled envs [EE-4859] (#8283) 2023-02-02 18:24:00 -03:00
LP B
921e9cfc6e fix(app/edge): updater/rollback calendar visual issues (#8386) 2023-02-02 12:57:53 -03:00
LP B
9b53960906 fix(api/edgestack): update deployments count when env relations are updated (#8433) 2023-02-02 12:04:58 -03:00
Oscar Zhou
402a62a5e2 fix(schedule): update date picker after removing edge group [EE-4963] (#8418) 2023-02-02 11:14:48 +13:00
cmeng
3470ea049a fix(update): prevent formik reinitialize [EE-4962] (#8426) 2023-02-02 09:59:17 +13:00
Ali
7fd263e8cc fix(texttip): fix texttip placement [EE-4990] (#8427)
Co-authored-by: testa113 <testa113>
2023-02-01 20:34:04 +13:00
Chaim Lev-Ari
36c6d3f21b fix(home): reduce update text for small screens [EE-4936] (#8422) 2023-02-01 12:14:53 +05:30
andres-portainer
5f3dd0a64f fix(edgestacks): fix edge stacks cache invalidation EE-4909 (#8399) 2023-02-01 02:16:04 -03:00
cmeng
42ca1287df fix(edge/stack): not clear stack status if version not updated [EE-4970] (#8408) 2023-02-01 09:18:04 +13:00
matias-portainer
2874a79279 fix(doc): update endpoint creation swagger documentation EE-4925 (#8415) 2023-01-31 11:06:27 -03:00
Ali
8574dd2371 fix(edge stacks): allow viewing existing kompose stacks [EE-4967] (#8405)
Co-authored-by: testa113 <testa113>
Co-authored-by: Matt Hook <hookenz@gmail.com>
2023-01-31 10:03:21 +13:00
Dakota Walsh
53eb5aa1ee fix(kube): 30 second delay to storage detection EE-4822 (#8360) 2023-01-31 09:58:57 +13:00
cmeng
eb8644330e fix(edge/job): init endpoints if it is null [EE-4972] (#8411) 2023-01-27 22:08:13 +13:00
cmeng
8663de580a fix(settings): EE-4959 Cannot turn on Edge Compute Features on CE (#8396) 2023-01-27 17:04:40 +13:00
Oscar Zhou
34298d96c5 fix: pass endpoint entity instead of endpoint.id (#8407) 2023-01-27 12:41:54 +13:00
cmeng
9d103ffbeb fix(UI): EE-4937 low resolution hides add container button (#8401) 2023-01-27 09:18:48 +13:00
Chaim Lev-Ari
5847c2b8ef fix(system/update): submit license form [EE-4743] (#8381) 2023-01-26 20:35:04 +05:30
matias-portainer
a09fe7e10c chore(edgejobs): AddEdgeJob disregards async mode EE-4855 (#8287) 2023-01-26 11:32:11 -03:00
Ali
5640cce4d6 chore(kompose): remove from settings [EE-4741] (#8375) 2023-01-26 16:03:44 +13:00
Chaim Lev-Ari
00bbf4ac63 refactor(auth): cache user data [EE-4935] (#8380) 2023-01-26 07:40:05 +05:30
matias-portainer
a748e15c16 feat(ui): bump codemirror version EE-4892 (#8393) 2023-01-25 10:31:12 -03:00
matias-portainer
cfdb9c126f fix(endpoints): check environment type before start metrics detection EE-4944 (#8391) 2023-01-25 10:29:11 -03:00
Chaim Lev-Ari
851a3346a9 fix(edge/update): remove schedule date for old envs [EE-3023] (#8315) 2023-01-24 12:20:55 +05:30
Oscar Zhou
c9aae27b29 fix(ci/security): update the node and golang version (#8387) 2023-01-24 14:41:00 +13:00
Prabhat Khera
087848539f fix(kubernetes): detect metrics API for kubernetes endspoints EE-4865 (#8351) 2023-01-24 09:05:15 +13:00
LP B
a74e389521 chore(dependencies): upgrade msw to fix xmldom CVE (#8362)
* chore(dependencies): upgrade msw to fix xmldom CVE

* refactor(msw): rename msw DefaultRequestBody to DefaultBodyType
2023-01-23 14:03:11 +01:00
Chaim Lev-Ari
eff6ec9df9 fix(edge/group): show tag selector when no tags [EE-4924] (#8368) 2023-01-23 11:05:23 +05:30
Prabhat Khera
8dec95c2cd chore(lint): add golangci linter to GitHub workflow EE-4872 (#8366) 2023-01-23 18:31:15 +13:00
Ali
5b02f636d7 fix(namespace): move warning [EE-4885] (#8370)
Co-authored-by: testa113 <testa113>
2023-01-23 10:41:39 +13:00
Prabhat Khera
ac458d0daa fix(ui): tooltip link color EE-4914 (#8365) 2023-01-23 10:15:47 +13:00
Chaim Lev-Ari
5b5dc320d5 fix(settings): save only the existing values [EE-4903] (#8326) 2023-01-23 02:34:39 +05:30
cmeng
d04747b309 fix(home): EE-4874 homepage ui issues (#8319) 2023-01-23 09:14:06 +13:00
LP B
07dd6bbe84 fix(home): dont display disconnected status similar to disabled (#8311) 2023-01-20 17:34:16 +01:00
Chaim Lev-Ari
406ff8812c feat(system/upgrade): add get license dialog [EE-4743] (#8249) 2023-01-19 15:31:49 +02:00
Chaim Lev-Ari
5942f4ff58 fix(home): dark mode for edit buttons [EE-4828] (#8241) 2023-01-19 10:16:34 +02:00
Prabhat Khera
adf92ce5e0 fix golang ci linter prehook (#8359) 2023-01-19 12:11:09 +13:00
cmeng
fed3d14adf fix(home): EE-4906 home page tiles for edge devices have the wrong url (#8353) 2023-01-19 09:00:43 +13:00
Prabhat Khera
73db588080 chore(linting): configure go linter EE-4871 (#8288) 2023-01-18 15:20:42 +13:00
Ali
6769326c8b fix(ingress): update label [EE-4902] (#8330)
Co-authored-by: testa113 <testa113>
2023-01-18 13:29:59 +13:00
Ali
e6d0e297dd fix(app): update redeploy wording [EE-4889] (#8317)
Co-authored-by: testa113 <testa113>
2023-01-18 08:30:06 +13:00
matias-portainer
0cd272211a fix(edgejobs): fix edge jobs log collection EE-4893 (#8328) 2023-01-17 14:21:13 -03:00
matias-portainer
6570f1f8eb fix(edgejobs): remove endpoint from edge job mapping on endpoint deletion EE-4764 (#8212) 2023-01-17 09:47:23 -03:00
Chaim Lev-Ari
1c180346e4 fix(ldap): sync user teams when needed [EE-4802] (#8235) 2023-01-16 10:41:32 +02:00
Chaim Lev-Ari
1d5d1bb12d fix(home): enable kubeconfig button [EE-4833] (#8233) 2023-01-15 11:32:38 +02:00
cmeng
0c27316034 fix(UI) EE-4883 stack repository method console error (#8304)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2023-01-13 16:07:52 +13:00
Chaim Lev-Ari
d3bed3072b fix(home): link to correct env route [EE-4877] (#8295) 2023-01-12 15:02:37 +02:00
Chaim Lev-Ari
329e8bcad5 fix(home): allow non-admins [EE-4873] (#8297) 2023-01-12 15:01:52 +02:00
Chaim Lev-Ari
4bdf30c038 feat(home): remove margins from env list [EE-4868] (#8285) 2023-01-11 10:59:56 +02:00
Matt Hook
7793b98813 straight not back ticks (#8292) 2023-01-11 14:54:50 +13:00
Matt Hook
02de7b2715 feat(version): add version setting script bump version EE-2529 (#6820)
* version bumper

* tell the user to check update
2023-01-11 14:07:18 +13:00
Chaim Lev-Ari
9c0e0607a4 fix(sidebar): put behind modal [EE-4866] (#8282)
* fix(sidebar): put behind modal

* fix(system/upgrade): validate on open dialog
2023-01-11 08:32:03 +13:00
Chaim Lev-Ari
baf9c3db0a feat(environments): add edge device [EE-4840] (#8246)
* feat(environments): add edge device [EE-4840]

fix [EE-4840]

* fix(home): fix tests
2023-01-11 08:30:49 +13:00
Prabhat Khera
6c193a8a45 refactor(log): log.fatal usage review EE-4607 (#8280) 2023-01-10 15:12:24 +13:00
Dakota Walsh
48a0f40621 fix(padding): fix margin-right on DatatableFooter pagination EE-4831 (#8237) 2023-01-10 10:14:46 +13:00
Matt Hook
4dc643acd9 fix(rolling-restart): wording and icon changes [EE-4834] (#8239)
* icon and wording changes

* fix inconsistencies and grammar

* fix(ui/buttons): show tooltip

* Change icon and confirmation dialog

* edit icon

* rename be-only-button to be-teaser-button for consistency

Co-authored-by: Chaim Lev-Ari <chaim.levi-ari@portainer.io>
2023-01-10 10:02:07 +13:00
Ali
1d42db93f1 fix(rbac): fix false negative rbac result in github microk8s environments [EE-4829] 2023-01-09 17:55:28 +13:00
andres-portainer
33c3f8460c Bump to v2.18.0. (#8266) 2023-01-07 12:08:23 -03:00
andres-portainer
dd0d1737b0 fix(performance): optimize performance for edge EE-3311 (#8040) 2023-01-06 16:25:41 -03:00
andres-portainer
3d28a6f877 chore(edgejobs): clean up EE-4850 (#8259) 2023-01-03 13:12:12 -03:00
andres-portainer
2fc518f221 chore(edgestacks): clean up EE-4851 (#8260) 2023-01-03 10:49:29 -03:00
andres-portainer
137ce37096 chore(nil): remove unnecessary nil checks EE-4847 (#8254) 2022-12-30 15:26:46 -03:00
andres-portainer
e529327851 chore(utils): remove dead code EE-4846 (#8253) 2022-12-30 14:52:58 -03:00
andres-portainer
3625ab6faa fix(rand): seed the RNG EE-4845 (#8252) 2022-12-30 14:52:18 -03:00
Chaim Lev-Ari
afb024d2a4 chore(docs): fix api reference [EE-4835] (#8242) 2022-12-25 10:47:21 +02:00
andres-portainer
b2bc4b92d6 fix(ui): remove extra margins from Dockerfile details section EE-4790 (#8213) 2022-12-21 19:09:40 -03:00
Chaim Lev-Ari
e5fd0c9595 fix(system): ignore failure to connect to docker [EE-4825] (#8231) 2022-12-21 18:08:18 +02:00
Chaim Lev-Ari
649c1c9cee feat(system): check BE image existence before upgrade [EE-4071] (#8230) 2022-12-21 17:17:51 +02:00
cmeng
919a854d93 feat(edge): EE-4570 allow pre-pull images with edge stack deployment (#8210)
Co-authored-by: Matt Hook <hookenz@gmail.com>
2022-12-21 13:18:51 +13:00
Chaim Lev-Ari
7fe0712b61 feat(home): move edge device to view [EE-4559] (#8189)
Co-authored-by: matias.spinarolli <matias.spinarolli@portainer.io>
2022-12-21 10:07:34 +13:00
Chaim Lev-Ari
b4a6f6911c fix(home): wrap items on small screens [EE-4798] (#8220) 2022-12-20 10:57:59 +02:00
Chaim Lev-Ari
59d35d26d8 feat(home): open env on click [EE-4792] (#8219) 2022-12-20 10:57:25 +02:00
Dakota Walsh
95558ed4ad fix(EE-4782): add portainer internal label to created ingress rules (#8196) 2022-12-20 16:46:51 +13:00
matias-portainer
e1b474d04f feat(edgejobs): support edge groups when using edge jobs EE-3873 (#8099) 2022-12-19 18:54:51 -03:00
Matt Hook
9732d1b5d8 remove kubeshell pod constraint (#8185) 2022-12-20 10:32:49 +13:00
Chaim Lev-Ari
701410d259 feat(app): clear env when log out [EE-4791] (#8218) 2022-12-19 08:56:39 +02:00
Chaim Lev-Ari
123754cee7 feat(system): make ui fixes to upgrade dialog [EE-4071] (#8199)
* feat(system): make ui fixes to upgrade dialog

* fix(system): hide on non-enabled platforms
2022-12-18 19:57:41 +02:00
Chaim Lev-Ari
d75d2ba9ce fix(docker): fix links to docs [EE-4486] (#8166) 2022-12-18 10:13:18 +02:00
Dakota Walsh
046738c967 feat(kubernetes): cluster setup reasonable defaults EE-4518 (#8082) 2022-12-16 16:03:40 +13:00
Matt Hook
0436be7bc4 update pull latest image wording (#8191) 2022-12-16 14:57:07 +13:00
cmeng
94d64997cc fix(compose): EE-4777 Toast Error displays insufficient details when creating stack in Standalone (#8208) 2022-12-16 14:43:24 +13:00
Chaim Lev-Ari
294d1668d4 feat(edge/updates): sync code changes from EE [EE-3023] (#8186)
* feat(edge/updates): add info about each field

* feat(edge/update): add beta feature

* fix(edge/update): add info
2022-12-16 14:20:22 +13:00
Chaim Lev-Ari
4bd6618fb9 chore(deps): upgrade docker binary [EE-4545] (#8146)
closes [EE-4545]
upgrades docker to v20.10.21

Co-authored-by: Yi Chen <69284638+yi-portainer@users.noreply.github.com>
2022-12-16 14:07:21 +13:00
Chaim Lev-Ari
62197a67f7 chore(deps): upgrade docker compose [EE-4642] (#8147)
closes [EE-4642]
upgrades docker compose to v2.13.0
2022-12-16 14:05:14 +13:00
Matt Hook
c1dc1b49d1 fix(pagination): vert center pagination control on all screens and add consistent spacing [EE-4006] (#8192) 2022-12-16 10:55:54 +13:00
andres-portainer
b917e12b62 fix(fileservice): add missing interface EE-3458 (#8207) 2022-12-15 17:21:58 -03:00
andres-portainer
a8ccd2b153 feat(filestore): add function to save mTLS certificates (#8206) 2022-12-15 16:16:29 -03:00
LP B
68975620c5 fix(app): cursor jumps to the end of inputs [EE-4359] (#8163)
* feat(app/react): introduce controlled inputs HOC to avoid creating uncontrolled React components

* fix(app/env): jumping inputs when adding gpus to existing environment
2022-12-15 17:49:36 +01:00
LP B
67d3abcc9d fix(app/analytics): exclude login page in analytics (#8172) 2022-12-15 17:48:59 +01:00
matias-portainer
90b0cb84f4 fix(wording): change wording on CE teaser EE-4590 (#8136) 2022-12-15 11:25:13 -03:00
Oscar Zhou
b22cdb3559 fix(access/team): team member cannot change ownership to its own team [EE-4423] (#8052) 2022-12-15 21:27:18 +13:00
andres-portainer
37896661d6 fix(edgestacks): avoid a data race in edge stack status update endpoint EE-4737 (#8168) 2022-12-14 10:41:45 -03:00
Chaim Lev-Ari
f38b8234d9 fix(edge/groups): show tags for environments [EE-4422] (#7940) 2022-12-14 09:17:49 +02:00
Chaim Lev-Ari
52e150fa29 fix(notifications): break lines for long message [EE-4521] (#8110) 2022-12-14 09:17:12 +02:00
Matt Hook
929749c0da incorrect variable name used (#8198) 2022-12-14 17:49:20 +13:00
Ali
09bf5d03f4 give docker specific tooltip link (#8197)
Co-authored-by: testa113 <testa113>
2022-12-14 17:26:19 +13:00
Ali
ac6f52ab76 fix(gitapp): set manifest in git settings [EE-4734] (#8190) 2022-12-14 14:30:15 +13:00
Dakota Walsh
0ddcad66f3 fix(auth): invalidate session when permissions change EE-3320 (#8103) 2022-12-14 10:12:00 +13:00
Chaim Lev-Ari
930d9e5628 feat(edge/stacks): use namespace in manifest [EE-4507] (#8145) 2022-12-13 22:56:47 +02:00
Chaim Lev-Ari
8936ae9b7a feat(home): add connect and browse buttons [EE-4182] (#8175) 2022-12-13 22:56:09 +02:00
Chaim Lev-Ari
db9d87c918 feat(system): path to upgrade swarm to BE [EE-4624] (#8124) 2022-12-13 22:52:06 +02:00
matias-portainer
b59a0ba823 fix(settings): fix settings save/update EE-3604 (#8180) 2022-12-12 15:11:12 -03:00
Matt Hook
2188005b48 feat(kubernetes): add rolling restart button teaser [EE-4510] (#8126)
* rolling restart teaser button

* add be only rolling restart

* move position of button
2022-12-12 14:30:05 +13:00
Prabhat Khera
a1528475ba feat(UI): tooltip html message support and width fix [EE-3445] (#8165)
Co-authored-by: testa113 <testa113>
2022-12-12 14:03:50 +13:00
Chaim Lev-Ari
5cbf52377d feat(system): path to upgrade standalone to BE [EE-4071] (#8095) 2022-12-11 08:58:22 +02:00
andres-portainer
756ac034ec fix(go): add Go workspace EE-4763 (#8182) 2022-12-09 13:23:01 -03:00
Oscar Zhou
1008afd1fe fix(session): reset the environment session storage after deleting it (#8119) 2022-12-09 11:11:24 +13:00
Ali
563ead85cc fix(gitoptions): git app edit ui tweaks [EE-4584] (#8159) 2022-12-09 10:41:11 +13:00
Chaim Lev-Ari
eba5879ec8 feat(home): change layout of env tile [EE-4479] (#8061) 2022-12-07 16:51:20 +02:00
matias-portainer
b48aa1274d fix(host): fix host info request EE-4641 (#8169) 2022-12-07 10:48:42 -03:00
Matt Hook
3e485c3152 feat(api): remove golang experimental packages [EE-3648] (#8081)
* remove golang experimental packages

* rebase and fix references
2022-12-07 17:15:52 +13:00
Oscar Zhou
dffd45c5f9 refactor(ui): extract TagButton from TagSelector component [EE-4194] (#8127) 2022-12-07 16:15:09 +13:00
Ali
c1cc8bad77 feat(rbac): detect if rbac is enabled [EE-4308] (#8139) 2022-12-07 15:53:06 +13:00
matias-portainer
8dcc5e4adb fix(edgestacks): fix repository edge stack creation EE-4732 (#8160) 2022-12-06 10:26:18 -03:00
Matt Hook
4558ce84cf fix(header): removed red dot from help menu [EE-4586] (#8133)
* remove red dot from help menu

* fix cursor, add menu-icon class back
2022-12-06 10:24:38 +13:00
Ali
adc87b8f8e feat(deployment): PO feedback [EE-4416] (#8143) 2022-12-06 08:49:26 +13:00
Chaim Lev-Ari
ce8455953e chore(deps): upgrade k8s client [EE-4543] (#8019) 2022-12-05 09:58:55 +02:00
Prabhat Khera
a61b18dd93 feat(kubernetes): fix wordings for ingresses toggle EE-2647 (#8151) 2022-12-05 10:47:56 +13:00
Matt Hook
d6a3fe23e9 feat(libhelm) update missed package paths [EE-4650] (#8134)
* add missing pkg paths

* fix go tests

* fixed pkg paths
2022-12-05 10:38:16 +13:00
Prabhat Khera
cbaba43842 fix(ui): tooltip stays open on hover [EE-3445] (#8051) 2022-12-05 09:47:43 +13:00
Matt Hook
c173888b64 remove old digest library and update path (#8141) 2022-12-02 10:46:59 +13:00
Chaim Lev-Ari
82e9e2a895 refactor(edge/updates): sync changes from EE [EE-4288] (#7726) 2022-12-01 08:40:52 +02:00
Matt Hook
4fee359247 feat(libhelm) import libhelm into CE pkg directory [EE-4650] (#8138)
* import libhelm and update some paths

* remove file

* update readme
2022-12-01 14:27:49 +13:00
Ali
9cdc0da615 feat(kompose): hide kompose [EE-4562] (#8084) 2022-12-01 13:46:23 +13:00
Matt Hook
8fd0efa34f add third_party digest lib to top level (#8140) 2022-12-01 13:26:39 +13:00
Matt Hook
79bfd8f6fe fix package import for docker/distribution (#8132) 2022-12-01 09:43:51 +13:00
Ali
2114c15f55 refactor(data-cy): duplicate data cy [EE-4656] (#8131) 2022-11-30 16:54:28 +13:00
Matt Hook
d2f6d1e415 import libhelm into portainer (#8128) 2022-11-30 14:25:47 +13:00
Matt Hook
241440a474 fix(fdo): import deleted digest library [EE-4654] (#8129)
* import digest lib

* update references

* fix lint errors
2022-11-30 09:11:49 +13:00
LP B
2e19f4ea6d fix(docker/container): auto select private access when enabling UAC on public container edit (#8032) [EE-44-64] 2022-11-29 11:24:13 +01:00
Matt Hook
95bc508462 fix(migrator): fix setting version struct fields after migration [EE-4613] (#8090)
* fix setting version struct fields

* fix go tests

* remove versionUpdateRequired

* remove old code that was originally for debugging purposes
2022-11-28 19:28:10 +13:00
Ali
d78b762f7b refactor(icons): replace fa icons [EE-4459] (#7907)
refactor(icons): remove fontawesome EE-4459

refactor(icon) replace feather with lucide EE-4472
2022-11-28 15:00:28 +13:00
andres-portainer
9dfac98a26 fix(tags): add missing error handling EE-4622 (#8102) 2022-11-24 14:16:04 -03:00
andres-portainer
e26a607d28 fix(edgegroups): avoid a last-write-wins situation when updating edge groups concurrently EE-3732 (#8101) 2022-11-23 21:36:17 -03:00
Dakota Walsh
6dc1841c14 fix(kube): disable namespace resource quota by default EE-4421 (#8080) 2022-11-23 15:01:41 +13:00
andres-portainer
c28be7aced fix(token-cache-manager): refactor to avoid data races EE-4438 (#8094) 2022-11-22 18:31:14 -03:00
Prabhat Khera
dd01165224 show secrets in kube applications datatable (#8065) 2022-11-23 08:48:36 +13:00
Chaim Lev-Ari
d484a0eb64 fix(docker): remove word break in details [EE-4481] (#7996) 2022-11-22 15:00:55 +02:00
Chaim Lev-Ari
fe8e834dbf refactor(ui/datatables): migrate views to use datatable component [EE-4064] (#7609) 2022-11-22 14:16:34 +02:00
Prabhat Khera
0f0513c684 feat(kubernetes): edit yaml support EE-2855 (#8016) 2022-11-22 09:40:44 +13:00
Chaim Lev-Ari
7006c17ce4 fix(wizard): debounce name state [EE-4177] (#8042)
move debouncing to the component (from the validation).
debounce returns undefined when it's not calling the debounced function,
and undefined is considered a validation error.
2022-11-21 19:33:08 +02:00
Chaim Lev-Ari
253a3a2b40 fix(ui): remove loading of missing interceptors [EE-4604] (#8086) 2022-11-21 17:25:09 +02:00
Chaim Lev-Ari
1e4c4e2616 refactor(edge): move edge codebase to react (#7781) 2022-11-21 09:51:55 +02:00
Chaim Lev-Ari
75f40fe485 refactor(portainer): remove offline mode [EE-4348] (#7761)
Co-authored-by: deviantony <anthony.lapenna@portainer.io>
2022-11-21 09:50:47 +02:00
Ali
61e8e68c31 fix(svg): fix footer height [EE-4547] (#8071) 2022-11-21 09:22:41 +13:00
Matt Hook
583346321e feat(version): migrate version to semver [EE-3756] (#7693)
redisigned version bucket and migration code
2022-11-18 13:18:09 +13:00
cmeng
4cfa584c7c fix(git): EE-4577 Git Repository Fields are Missing in Edge Stacks (#8057) 2022-11-18 08:59:58 +13:00
Ali
d012a4efc4 feat(deployment): enforce deployment options EE-4416 (#7974) 2022-11-17 22:00:34 +13:00
Oscar Zhou
e0f3a8c0a2 fix(access/viwer): update the viwer filter key to user.id (#8055) 2022-11-17 10:38:34 +13:00
Yi Chen
bb48ab00cb * remove empty examples (#7952)
* fix missing type
2022-11-17 06:55:08 +13:00
Chaim Lev-Ari
eccc8131dd feat(system/upgrade): add upgrade banner [EE-4564] (#8046) 2022-11-16 18:38:39 +02:00
matias-portainer
c21921a08d fix(edgestacks): return 400 instead of 500 on edge stack create when an invalid payload is provided EE-4429 (#7880) 2022-11-15 17:40:56 -03:00
Prabhat Khera
573e05d1c7 feat(kubernetes): BE teasure added EE-2647 (#7927) 2022-11-15 20:57:01 +13:00
Prabhat Khera
246e351817 remove wrong checked-in files from parent dir (#8048) 2022-11-15 09:01:28 +13:00
Prabhat Khera
6775c7b6ec clean database models directory (#8026) 2022-11-14 08:33:57 +13:00
Chaim Lev-Ari
881e99df53 refactor(nomad): sync frontend with EE [EE-3353] (#7758) 2022-11-13 12:29:25 +02:00
Chaim Lev-Ari
78dcba614d refactor(portainer): move to react [EE-3350] (#7915) 2022-11-13 10:10:18 +02:00
matias-portainer
30e23ea5b4 fix(ui): tidy up Edge Devices configuration EE-3604 (#7797) 2022-11-10 16:13:20 -03:00
Oscar Zhou
e1e81731b9 fix(setting/auth): allow to save all auth method if custom ldap server is empty string (#7990) 2022-11-10 08:28:09 +13:00
Oscar Zhou
16377221f9 fix(stack): check if endpoint exists before checking the user permission on the endpoint (#7967) 2022-11-09 12:20:19 +13:00
Chaim Lev-Ari
a0237852ef fix: fix occurred typo [EE-4536] (#7994) 2022-11-08 07:30:37 +02:00
andres-portainer
193e0c7d6f fix(snapshots): remove snapshots when removing endpoints EE-4527 (#7973)
* fix(snapshots): remove snapshots when removing endpoints EE-4527

* Fix nil pointer dereference.

Co-authored-by: andres-portainer <andres-portainer@users.noreply.github.com>
2022-11-07 20:28:18 -03:00
Chaim Lev-Ari
77c29ff87e refactor(kubernetes): move react codebase [EE-3349] (#7953) 2022-11-07 08:03:11 +02:00
Chaim Lev-Ari
2868da296a fix(stack): validate original containers names [EE-4520] (#7978) 2022-11-06 10:40:33 +02:00
Chaim Lev-Ari
ff10588383 fix(edge): show select all checkbox [EE-4129] (#7948) 2022-11-06 09:33:22 +02:00
LP B
6b02d9a1e3 fix(app/logs): change pattern to detect double serialized JSON logs [EE-4525] (#7962)
* fix(app/logs): change pattern to detect double serialized JSON logs

* fix(app/logs): fallback to raw display when parsing fails + include timestamp for Zerolog logs
2022-11-04 13:58:18 +01:00
Ali
9f3d5185b0 fix(slider): use and update react slider EE-4522 (#7987) 2022-11-04 14:12:53 +13:00
congs
f94147b07b fix(registry): EE-4526 Registry Manage access link broken (#7975) 2022-11-04 12:10:42 +13:00
andres-portainer
49d02e0386 fix(db): remove a bucket overwrite EE-4424 (#7867) 2022-11-03 18:51:15 -03:00
Ali
e82d0cfbdb fix(apps): fix edit error EE-4529 (#7963) 2022-11-03 13:04:56 +13:00
Oscar Zhou
c8051b68d4 fix(auth/ldap): allow to save internal authentication if custom ldap server is not set [EE-3155] (#7959) 2022-11-03 10:06:56 +13:00
Chaim Lev-Ari
37d4a80769 refactor(environments): remove endpoints cache [DTD-100] (#6408) 2022-11-02 13:29:26 +02:00
Chaim Lev-Ari
9ef2e27aae fix(environments): debounce name validation [EE-4177] (#7889) 2022-11-02 12:44:31 +02:00
Oscar Zhou
9e1f80cf37 chore(ui/ldap): add texttip for group search configuration (#7944) 2022-11-02 11:31:44 +13:00
Dakota Walsh
459c95169a fix(ingresses): migrate to new allow/disallow format EE-4465 (#7893) 2022-11-02 11:17:32 +13:00
fhanportainer
5048f08b5f fix(box-selector): fixed incorrect wording for Private Box selector under UAC. (#7949) 2022-11-01 11:06:11 +13:00
fhanportainer
e785d1572e fix(web-editor): fixed web editor scroll bar. (#7941) 2022-10-31 11:03:50 +13:00
Rex Wang
95a4f83466 fix(docker): docker template UI bug fix [EE-4034] (#7912)
* EE-4034 fix(docker): docker template UI bug fix

* EE-4034 fix(docker): fix ui
2022-10-30 14:56:23 +08:00
Dmitry Salakhov
4edf232e41 fix: document edge endoint url requirement (#7735) [EE-3425] 2022-10-28 13:00:12 +13:00
Dmitry Salakhov
903cf284e7 fix(image): build image from file (#7929) [EE-4501] 2022-10-27 23:31:31 +13:00
Prabhat Khera
a550bfaedb fix showing namespaces for standard user (#7917) 2022-10-27 16:14:54 +13:00
Hao
446febb0f6 fix(image): hide button issues [EE-4166] (#7845)
* fix(image): hide button issues [EE-4166]
2022-10-25 15:02:59 +08:00
Oscar Zhou
cb9fe2606c fix(team): disable team leader setting when external auth sync is enabled [EE-3579] (#7852) 2022-10-25 14:39:24 +13:00
Dakota Walsh
55211ef00e fix(ingress): allow none controller type EE-4420 (#7883)
Co-authored-by: testA113 <alex.harris@portainer.io>
2022-10-25 09:41:30 +13:00
Chaim Lev-Ari
e48ceb15e9 refactor(environments): move environments ts code to react [EE-3443] (#7747) 2022-10-23 09:53:25 +03:00
Rex Wang
1b12cc9f31 EE-4376 fix(docker): fix malformed struct of template (#7803) 2022-10-21 16:29:18 +08:00
Hao
0365ed8e70 fix(docker): comfirm modal for removing secrets/networks/configs [EE-4211] (#7882)
* fix(ui): comfirm modal for removing secrets/networks/configs [EE-4211]

* fix(ui): comfirm modal for removing secrets/networks/configs [EE-4211]

* fix(ui): comfirm modal for removing secrets/networks/configs [EE-4211]
2022-10-21 16:03:41 +08:00
Chaim Lev-Ari
7624ff10ee chore(edge): add aria-label for edge-group selector [EE-4466] (#7896)
* chore(edge): add aria-label for edge-group selector

* style(edge): remove comment
2022-10-21 08:22:49 +03:00
andres-portainer
535a26412f fix(logging): default to pretty logging [EE-4371] (#7847)
* fix(logging): default to pretty logging EE-4371

* feat(app/logs): prettify stack traces in JSON logs

* feat(nomad/logs): prettify JSON logs in log viewer

* feat(kubernetes/logs): prettigy JSON logs in log viewers

* feat(app/logs): format and color zerolog prettified logs

* fix(app/logs): pre-parse logs when they are double serialized

Co-authored-by: andres-portainer <andres-portainer@users.noreply.github.com>
Co-authored-by: LP B <xAt0mZ@users.noreply.github.com>
2022-10-20 16:33:54 +02:00
Chaim Lev-Ari
ee5600b6af chore(build): incremental ts build [EE-4204] (#7888) 2022-10-20 17:23:56 +03:00
Rex Wang
3f51d077ac fix(docker): create custom template [EE-4114] (#7812)
* EE-4114 fix(docker): create custom template

* Update customtemplate_create.go

remove white space
2022-10-20 16:42:49 +08:00
Rex Wang
0219d41ba7 fix(docker): Show stopped container on dashboard [EE-4327] (#7833)
* EE-4327 fix(docker): Show stopped container on dashboard

* Update ContainerStatus.tsx

remove comment

* EE-4327 fix(docker): show stopped container on dashboard
2022-10-20 15:10:39 +08:00
Prabhat Khera
f3e2ccd487 fix volume claims with k8s app (#7899) 2022-10-20 15:43:19 +13:00
Ali
368e6b2a44 fix(helm): helm charts view bad icon aspect ratio EE-4451 (#7875) 2022-10-20 14:14:55 +13:00
Dmitry Salakhov
1100a2bd28 feat: move jwt lib to v4 (#7773) 2022-10-20 10:26:11 +13:00
itsconquest
16dc66f173 fix(UAC): put team into resource control when editing as team lead [EE-4457] (#7886)
* fix(UAC): put team into resource control when editing as team lead [EE-4457]

* populate form values & payload correctly
2022-10-20 10:18:56 +13:00
itsconquest
c1f94be9b2 fix(notifications): sort by newest first by default [EE-4467] (#7891) 2022-10-19 15:25:20 +13:00
Matt Hook
58947fee69 fix(libhelm): new libhelm with relaxed validation when adding chart repo [EE-4440] (#7874)
update helm to fix adding some chart repos
2022-10-19 12:43:33 +13:00
Dakota Walsh
0c995ae1c8 fix(kubernetes): create proxied kubeclient EE-4326 (#7850) 2022-10-18 10:46:27 +13:00
itsconquest
f6d6be90e4 fix(UAC): provide required UI context [EE-4415] (#7854) 2022-10-18 09:45:47 +13:00
andres-portainer
5488389278 fix(code): replace calls to ioutil EE-4425 (#7878) 2022-10-17 15:29:12 -03:00
andres-portainer
69f498c431 fix(tests): add missing context cancel EE-4433 (#7879) 2022-10-17 13:57:41 -03:00
Prabhat Khera
669327da7c fix reloading page when ing class disallowed (#7830) 2022-10-17 10:44:17 +13:00
andres-portainer
191f8e17ee fix(code): remove unused code EE-4431 (#7866) 2022-10-14 19:42:31 -03:00
andres-portainer
ae2bec4bd9 fix(code): clean up EE-4432 (#7865) 2022-10-14 18:09:07 -03:00
andres-portainer
367f3dd6d4 fix(tags): remove a data race EE-4310 (#7862) 2022-10-13 11:12:12 -03:00
matias-portainer
8f1ac38963 fix(tags): get tags when loading associated endpoints selector EE-4140 (#7857) 2022-10-13 11:03:19 -03:00
Ali
7a6ff10268 fix(ing): nodeport validate and show errors [EE-4373] (#7801) 2022-10-12 10:06:57 +13:00
andres-portainer
fd91de3571 fix(logging): remove remaining traces of logrus EE-4414 (#7848) 2022-10-11 16:53:27 -03:00
andres-portainer
ab3a6f402e fix(build): add -trimpath EE-4406 (#7836) 2022-10-11 13:00:50 -03:00
andres-portainer
d3edb7ebd5 fix(logging): convert missing cases to Zerolog EE-4400 (#7817) 2022-10-11 12:59:00 -03:00
Oscar Zhou
c23b8b2816 fix(gitops): update the git ref cache key from url to url and pat (#7841) 2022-10-11 18:31:21 +13:00
itsconquest
724f1f63b7 fix(notifications): cleanup notifications code [EE-4274] (#7790)
* fix(notifications): cleanup notifications code [EE-4274]

* break long words
2022-10-11 14:05:53 +13:00
Ali
c6ae8467c0 fix(ingress): update ingress tls after deletion EE-4387 (#7804)
* fix(ing): update tls value EE-4387
2022-10-10 09:32:30 +13:00
Ali
56087bcbb3 fix(clustersetup): dont show modal when loading (#7810) 2022-10-08 17:48:36 +13:00
Ali
315c1c7e1e fix(application): edit cluster ip services EE-4328 (#7775) 2022-10-07 16:55:11 +13:00
congs
819dc4d561 fix(UI): EE-4381 environment ID is shown instead of its name when deleting an environment (#7808) 2022-10-07 16:36:19 +13:00
congs
380a64d546 fix(wizard): EE-4350 Environment creating script should only showed for relevant type of environment (#7786) 2022-10-07 15:43:06 +13:00
congs
6429546462 fix(help): EE-4335 context sensitive help improvement (#7754) 2022-10-07 14:25:26 +13:00
matias-portainer
ebfb71da05 fix(edge): fix docker proxy EE-4380 (#7799) 2022-10-06 11:12:39 -03:00
Ali
ae0b9b1e30 fix(ingress): ingress indicate missing services EE-4358 (#7794) 2022-10-06 15:24:59 +13:00
Oscar Zhou
e9de484c3e refactor(stack): stack build process backend only [EE-4342] (#7750) 2022-10-05 22:33:59 +13:00
Prabhat Khera
83a1ce9d2a bug(ingress): fix ingress class disallowed to not found issue EE-4311 (#7731) 2022-10-05 15:17:53 +13:00
Rex Wang
66fd039933 EE-2681 fix(docker): fix message format (#7784) 2022-10-05 09:20:00 +08:00
Ali
1722257d68 fix(cluster): fix cluster setup no ingress develop EE-4352 (#7776)
* fix(cluster) update cluster wo controllers EE-4352

* fix(ing): stop errors in ns EE-4352
2022-10-04 12:13:56 +13:00
Ali
7d8b037761 fix(deploy): update option text EE-4362 (#7783) 2022-10-04 10:20:16 +13:00
Ali
cd52e04a5a fix(customtemplate) fix custom var payload EE-4340 (#7752) 2022-10-03 09:49:28 +13:00
Xuing
a0fa64781a fix(readme) update deploy portainer url (#7760) 2022-09-30 14:47:47 +13:00
Ali
43e3cb476b fix(clustersetup): set a default access mode (#7745) 2022-09-29 10:26:25 +13:00
Ali
a1a88eb5e4 fix(secrets): allow edit sa token, refactor (#7732) 2022-09-29 09:57:39 +13:00
andres-portainer
cb79dc18f8 chore(code): reduce divergence with EE EE-4344 (#7748) 2022-09-28 14:56:32 -03:00
andres-portainer
e9384a6987 fix(database): fix a data race around the bucket sequences EE-4332 (#7738) 2022-09-27 16:14:00 -03:00
Dmitry Salakhov
90a0e6fe35 bump release version (#7733) 2022-09-27 13:55:20 +13:00
LP B
e5f8466fb9 fix(app/environments): retain previously selected environments [EE-3233] (#7358) 2022-09-26 19:00:10 -03:00
Yi Chen
c3110a85b2 * replace npm mirror with yarnpkg (#7730) 2022-09-27 10:08:47 +13:00
Dakota Walsh
89eda13eb3 feat(ingress): autodetect ingress controllers EE-673 (#7712) 2022-09-27 08:43:24 +13:00
Hao
c96551e410 feat(stack): rebuild image for compose stack from git [EE-2681] (#7707)
* feat(stack): rebuild image for compose stack from git [EE-2681]

* feat(stack): rebuild image for compose stack from git [EE-2681]

* --no-edit

* UI
2022-09-26 14:22:38 +08:00
Rex Wang
9f7d5ac842 fix(docker): stack's env vars support empty value EE-1528 (#7592)
* EE-1528 fix(docker): stack's env vars support empty value

* EE-1528 fix(docker): handle no-value env as empty env
2022-09-24 20:05:20 +08:00
itsconquest
648c1db437 feat(notifications): track toast notifications [EE-4132] (#7711)
* feat(notifications): track toast notifications [EE-4132]

* suggested refactoring

* fix failing test

* remove duplicate styles

* applying spacing to context icon
2022-09-23 17:17:44 +12:00
Ali
4e20d70a99 feat(secrets): allow creating secrets beyond opaque [EE-2625] (#7709) 2022-09-23 16:35:47 +12:00
fhanportainer
3b2f0ff9eb fix(access-token): fixed create access token view. (#7716) 2022-09-23 16:29:25 +12:00
Prabhat Khera
fcb76f570e feat(ingress): remove ingresses from add and edit application EE-4206 (#7677) 2022-09-23 16:11:35 +12:00
itsconquest
c384d834f5 fix(build): restore aliases for uppercase imports [EE-4312] (#7723) 2022-09-23 15:55:05 +12:00
Dmitry Salakhov
45e2ed3d86 fix: miscofigured logging statements (#7721) 2022-09-23 13:15:26 +12:00
matias-portainer
6e0f83b99e feat(snapshots): separate snapshots from endpoint DB struct EE-4099 (#7614) 2022-09-22 17:05:10 -03:00
Prabhat Khera
4fe2a7c750 fix ingress screen loading (#7715) 2022-09-22 16:12:19 +12:00
congs
f8b8d549fd feat(help): EE-2724 Context sensitive help (#7694) 2022-09-22 13:39:36 +12:00
LP B
1b0db4971f feat(app/logs): format Zerolog in logs viewer [EE-4226] (#7685)
* feat(app/logs): format Zerolog in logs viewer

* fix(app/logs): trim caller to only last 2 segments
2022-09-22 00:34:58 +02:00
LP B
6063f368ea fix(api/snapshot): convert error message only on matching env types (#7661) 2022-09-22 00:34:14 +02:00
Chao Geng
8ef584e41c feat(docker): new version message in BE side menu [EE-4079] (#7680)
* export GetLatestVersion and HasNewerVersion
2022-09-21 17:22:39 +08:00
Chaim Lev-Ari
ceaee4e175 refactor(ui): replace ng selectors with react-select [EE-3608] (#7203)
Co-authored-by: LP B <xAt0mZ@users.noreply.github.com>
2022-09-21 10:10:58 +03:00
Chaim Lev-Ari
1e21961e6a refactor(app): move settings components to react [EE-3442] (#7625) 2022-09-21 09:14:29 +03:00
Oscar Zhou
5777c18297 feat(gitops): support to list git repository refs and file tree [EE-2673] (#7100) 2022-09-21 17:47:02 +12:00
Prabhat Khera
ef1d648c07 feat(ingress): ingresses datatable with add/edit ingresses EE-2615 (#7672) 2022-09-21 16:49:42 +12:00
Dmitry Salakhov
393d1fc91d fix: braking changes in compose (#7708) [EE-4258] 2022-09-21 15:59:40 +12:00
andres-portainer
f9fe440401 feat(logging): trim paths from the build EE-4186 (#7710) 2022-09-20 18:48:07 -03:00
Chaim Lev-Ari
fad376b415 refactor(ui): remove global providers [EE-4128] (#7578) 2022-09-20 21:14:24 +03:00
Chao Geng
d3f094cb18 fix(image): better URL info text and a link to documentation to build image [EE-2409] (#7641)
* URL info text and a link to documentation to build image
2022-09-20 13:42:31 +08:00
Matt Hook
1950c4ca2b Sanitze kube labels (#7658) 2022-09-20 16:19:54 +12:00
Chao Geng
5232427a5b updated k8s stack deployment specification in Swagger (#7619) 2022-09-20 06:59:14 +08:00
andres-portainer
0fac1f85f7 feat(logging): redirect the standard logger to Zerolog EE-4186 (#7702) 2022-09-19 15:39:43 -03:00
Hao
70ce4e70d9 fix(registry): fix anonymous dockerhub name to make it same with BE [EE-4208] (#7690) 2022-09-19 16:52:15 +08:00
congs
47f2490059 fix(wizard) EE-2053 Add Docker Standalone option to agent install instructions (#7589) 2022-09-19 13:44:52 +12:00
Chaim Lev-Ari
4d123895ea feat(edge/update): select endpoints to update [EE-4043] (#7602) 2022-09-18 14:42:18 +03:00
andres-portainer
36e7981ab7 feat(logging): replace all the loggers with zerolog EE-4186 (#7663) 2022-09-16 13:18:44 -03:00
Oscar Zhou
53025178ef fix(access): support to list users or teams with specified endpoint [EE-1704] (#7610) 2022-09-16 14:45:14 +12:00
Rex Wang
f71fe87ba7 fix(docker): ui link style [EE-4184] (#7655)
* EE-4184 fix(docker): ui link style
2022-09-15 17:33:49 +08:00
congs
6078234d07 fix(stack): EE-4213 Allow latest image to be pulled for stacks: backport backend logic (#7669) 2022-09-15 16:57:26 +12:00
Oscar Zhou
fa162cafc1 feat(gitops): support to store git credentials [EE-2683] (#7066) 2022-09-15 16:32:05 +12:00
andres-portainer
9ef5636718 chore(handlers): replace structs by functions for HTTP errors EE-4227 (#7664) 2022-09-14 20:42:39 -03:00
Matt Hook
7accdf704c fix(kube): change warning text colour to match figma styling [EE-3045] (#7582)
* update warning text colour, icon and alignment to match figma
2022-09-15 11:09:19 +12:00
Chao Geng
d570aee554 feat(image): upload local files for building image EE-3021 (#7507)
* support to make multiple files in archive buffer

* upload files by multipart
2022-09-14 14:47:24 +08:00
Chao Geng
a7d458f0bd chore(tests): use t.TempDir to create temporary test directory [EE-3700] (#7612)
* create temporary test directory with t.TempDir
2022-09-14 13:59:47 +08:00
congs
1a9d793f2f fix(stack): EE-4213 Allow latest image to be pulled for stacks (#7653) 2022-09-14 10:17:32 +12:00
fhanportainer
0242c8e4ef fix(dropdown): fixed dropdown menu background color in dark mode. [EE-4026] (#7591)
* fix(dropdown): fixed dropdown menu background color in dark mode. [EE-4026]

* fix(dropdown): fixed table setting background color in dark mode.

* fix(dropdown): updated --bg-dropdown-menu-color in dark theme.

* fix(dropdown): fixed dropdown border radius issue

* fix(dropdown): fixed dropdown option text color in dark mode
2022-09-14 10:16:02 +12:00
Chaim Lev-Ari
6c4c958bf0 feat(edge/update): remote update structure [EE-4040] (#7553) 2022-09-13 16:56:38 +03:00
itsconquest
dd1662c8b8 fix(extension): change ports to reduce conflicts [EE-3211] (#7596) 2022-09-13 11:03:37 +12:00
LP B
fdfebcf731 fix(style): autofilled inputs use theme colors [EE-3828] (#7576) 2022-09-12 16:29:15 +02:00
itsconquest
9ce3e7d20d fix(theme): tabs and codeeditor darkmode correction [EE-4188] (#7643)
* fix(theme): tabs and codeeditor darkmode correction [EE-4188]

* correct codemirror background

* fix typo
2022-09-12 17:07:03 +12:00
congs
bf8b9463d3 fix(security): EE-3202 Portainer CE and EE JS Dependencies (#7561) 2022-09-12 13:32:58 +12:00
Oscar Zhou
9375e577b0 feat(setting): display custom banner option as the limited feature for be (#7590) 2022-09-09 13:29:30 +12:00
itsconquest
d95a67a567 fix(theme): env sidebar darkmode color [EE-4188] (#7638)
* fix(theme): env sidebar darkmode color [EE-4188]

* style usericon

* further dark mode changes
2022-09-09 12:47:06 +12:00
Dmitry Salakhov
160e210ffe feat: update compose and helm versions (#7536) [EE-3205] 2022-09-09 11:26:56 +12:00
itsconquest
c9eaad6237 fix(auth): prevent trim on password [EE-4197] (#7633) 2022-09-08 13:50:21 +12:00
itsconquest
2edff939ef fix(theme): update dark mode colors [EE-4188] (#7623)
* fix(theme): update dark mode colors [EE-4188]

* fix sidebar hover/selected
2022-09-08 13:49:09 +12:00
congs
13338c46bb fix(wizard): EE-3728 Metadata is not working with Nomad (#7615) 2022-09-08 13:11:57 +12:00
LP B
ea05814af4 fix(images/build): enforce file content length only when using the editor (#7630) 2022-09-08 02:32:36 +02:00
Dmitry Salakhov
0fe2ddf535 fix: don't url-escape socket paths (#7627) 2022-09-08 11:44:50 +12:00
Rex Wang
9af9395b73 fix(docker): prevent misconfigured stack from saving EE-3235 (#7585)
* EE-3235 fix(docker): add checker to editor

* support rollback to update stack file

Co-authored-by: chaogeng77977 <chao.geng@portainer.io>
2022-09-07 16:50:59 +08:00
Chaim Lev-Ari
d9cc7eda51 refactor(app): move access-control components [EE-3441] (#7559) 2022-09-07 07:25:00 +03:00
fhanportainer
77c3f9131b fix(environment): update page title when no environment selected. (#7606)
* fix(environment): update page title when no environment selected.

* fix(environment): update page title when clearing environment from side bar.

* fix(environment): update page title when clearing environment from a non-environment page.
2022-09-07 11:08:45 +12:00
Dakota Walsh
2b2580fb61 fix(kubernetes): gke node stats (#7455) 2022-09-07 10:39:00 +12:00
congs
f870619fb6 fix(git): EE-3727 nomad extension not available (#7595) 2022-09-06 10:54:21 +12:00
LP B
602e42739e feat(stacks): remove the ability to delete external swarm stacks [EE-2611] (#7560) 2022-09-05 15:00:49 +02:00
Rex Wang
326a8abdc7 EE-4021 fix(docker): rename deployed container (#7601) 2022-09-05 17:39:08 +08:00
Rex Wang
c0f3d0193d EE-4125 fix(docker): fix creating container UI style (#7607) 2022-09-05 07:08:38 +08:00
Chaim Lev-Ari
f9427c8fb2 refactor(teams): migrate teams to react [EE-2273] (#6691)
closes [EE-2273]
2022-09-02 18:30:34 +03:00
huib-portainer
9b02f575ef chore(readme): update readme to remove the outdated demo 2022-09-02 13:53:47 +12:00
itsconquest
5b4f6098d8 fix(boxselector): fix darkmode BE teaser style [EE-4145] (#7598)
* fix(boxselector): fix darkmode BE teaser style [EE-4145]

* make opacity same when selected

* add missing link to teaser

* style unchecked boxes + light mode

* revert colors for ligh theme
2022-09-02 12:42:48 +12:00
Oscar Zhou
ccaf2bedb7 fix(stack/compose): remove the orphan containers if stack deployment is failed (#7599) 2022-09-02 08:11:02 +12:00
Rex Wang
88757d2617 fix(docker): style fixes [EE-4024] (#7569)
* EE-4042 update docker screens trash icon

* EE-4024 fix(docker): change styles
2022-09-01 19:02:21 +08:00
Matt Hook
d79586cf6a chore(readme): update readme to display latest version (#7604)
* use badge to display latest version 
* use markdown syntax
2022-09-01 14:04:59 +12:00
Rex Wang
a9b1a9c194 fix(docker): don't trimming when creating secret [EE-3265] (#7577)
* EE-3265 fix(docker): stop trimming when creating secret

* EE-3265 fix(docker): stop triming when creating secret in k8s
2022-08-31 23:19:14 +08:00
fhanportainer
eb5036b96f fix(docker): removed docker.sock code in docker API [EE-3612] (#7586) 2022-08-31 20:32:01 +12:00
LP B
2f0dbf2ae1 fix(container/edit): fallback value when retrieving GPU config without snapshot available [EE-4110] (#7570) 2022-08-30 14:52:24 +02:00
itsconquest
c79be58700 fix(sidebar): rework the update notification [EE-4119] (#7575) 2022-08-30 10:00:12 +12:00
Oscar Zhou
d24e5ff71e feat(docker/container): support --shm-size configuration [EE-550] (#7547) 2022-08-30 09:22:27 +12:00
Chaim Lev-Ari
6536d36c24 feat(ui): hide user menu on docker extension [EE-4115] (#7563) 2022-08-29 05:07:07 +03:00
wheresolivia
6174940ac2 add data-cy attributes for docker image tag selectors (#7581) 2022-08-29 13:46:06 +12:00
fhanportainer
4c98fcd7db feat(analytis): EnableTelemetry defaults to false (#7539) 2022-08-29 11:09:47 +12:00
congs
ad8054ac1f fix(stack): EE-3908 broken modal when updating/redeploying stacks: turn off toggle (#7573) 2022-08-26 17:54:10 +12:00
Matt Hook
a54c54ef24 fix(swarm): fixed issue parsing url with no scheme [EE-4017] (#7502) 2022-08-26 11:55:55 +12:00
itsconquest
27095ede22 fix(stacks): orphaned stacks readonly [EE-4085] (#7552)
* fix(stacks): orphaned stacks readonly [EE-4085]

* correctly handle stack type in controller
2022-08-25 10:27:12 +12:00
congs
e2789ab354 fix(container): EE-3995 gpus console error under stack list page (#7530) 2022-08-25 10:27:02 +12:00
Matt Hook
d4f4bb532f fix(web-editor): add search hint text [EE-3967] (#7496) 2022-08-25 10:11:25 +12:00
Zhang Hao
c6ab5d5717 fix(image): Add hide default registry teaser for CE version [EE-4038] (#7533)
* fix(image): Add hide default registry teaser for CE version [EE-4038].

* fix(image): Hide advanced mode only if there is no docker hub registries [EE-3734]

* sync with EE
2022-08-24 19:33:48 +08:00
Chaim Lev-Ari
234627f278 fix(ui/buttons): set hyperlink style [EE-4007] (#7524) 2022-08-24 07:40:50 +03:00
itsconquest
87214d48be fix(wizard): highcontrast style for BE only options (#7544) 2022-08-24 14:48:05 +12:00
itsconquest
a2a35a1851 fix(azure): correctly sort container ports [EE-4076] (#7550) 2022-08-24 12:43:04 +12:00
Chaim Lev-Ari
11f0574ad3 fix(stack): hide containers for swarm stack [EE-3969] (#7504) 2022-08-23 09:47:09 +03:00
Chaim Lev-Ari
9fbc6177a6 fix(stack): hide containers for swarm stack [EE-3969] (#7503) 2022-08-23 09:46:55 +03:00
congs
b91e06a60a fix(stack): EE-3908 broken modal when updating/redeploying stacks (#7497) 2022-08-23 14:22:18 +12:00
fhanportainer
ad3f4ff711 fix(toggle): fixed disabled toggle color in dark and high contrast modes. (#7518)
* fix(toggle): fixed disabled toggle color in dark and high contrast modes.

* fix(switch): fixed switch color in dark and high contrast modes.

* fix(switch): fixed switch in LDAP secion.

* fix(switch): corrected the blue color of Switch in dark and high contrast themes.
2022-08-23 12:11:11 +12:00
Prabhat Khera
7edcfd6eab fix minor ui issues (#7510) 2022-08-23 08:55:41 +12:00
Prabhat Khera
735b2063ea fix(ui): minor ui issues EE-4004 (#7512) 2022-08-23 08:54:40 +12:00
matias-portainer
bce4d02dd2 fix(edge): save edge checkin interval during endpoint creation (#7541) 2022-08-22 12:08:53 -03:00
fhanportainer
e84126ec13 feat(label): uses --ui-white for control-label css class in Dark and High contrast themes (#7505)
* feat(label): uses `--ui-white` for control-label css class in Dark and High contrast themes.

* feat(label): uses `apply` in control-label css class.
2022-08-23 03:08:29 +12:00
matias-portainer
3a324acb0e fix(edge): fix edge URL placeholder EE-2598 (#7459) 2022-08-22 10:13:01 -03:00
Rex Wang
c6f7427283 Fix(UI) fix color of file upload button in dark mode EE-4009 (#7535)
* fix snapshot url parsing issue for ip addresses (#7477)

* fix(ui/header): change font sizes [EE-3966] (#7484)

* fix(k8s/apps): show horizontal scrollbar [EE-3941] (#7476)

* fix(kubeconfig): update button and modal styles (#7480)

EE-3947

* fix(containers): make table wider [EE-3944] (#7486)

* Fix(UI) UI fixes on docker container screens (release/2.15) EE-3915 (#7499)

* EE-3915 ui fixes on docker container screens

* Update createcontainer.html

Update label

* EE-3916 fix container link under stack detail page (#7508)

* EE-4009 fix color of file-upload button in dark mode

Co-authored-by: Matt Hook <hookenz@gmail.com>
Co-authored-by: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Co-authored-by: Ali <83188384+testA113@users.noreply.github.com>
2022-08-22 20:25:26 +08:00
Chaim Lev-Ari
ace01eac9d fix(ui): box-selector fixes [EE-3949] (#7489) 2022-08-22 11:55:48 +03:00
Ali
8d304b78cb fix(kubeshell): add back data-cy EE-4054 (#7538) 2022-08-22 16:52:24 +12:00
fhanportainer
c17baa36ef fix(app-template): fixed the app template list not scroll to top issue (#7519)
* fix(app-template): fixed the app template list not scroll to top issue

* fix(templates): added id prop to PageHeader component.
2022-08-20 00:31:17 +12:00
fhanportainer
8cbff097e4 feat(docker): fixed info icon in docker feature config section. (#7492) 2022-08-19 12:55:29 +12:00
Matt Hook
294738cb0d bump version to 2.16 (#7475) 2022-08-19 10:34:47 +12:00
Rex Wang
69bc815acd EE-3998 bug fix (#7522) 2022-08-18 18:56:37 +08:00
Prabhat Khera
fb62edefbc fix background (#7515) 2022-08-18 22:39:54 +12:00
fhanportainer
5e35ff8b8a feat(stack): fixed stack web editor scroll bar issue. (#7494) 2022-08-18 18:00:33 +12:00
Rex Wang
20053b1f07 EE-3916 fix container link under stack detail page (#7509) 2022-08-17 23:48:21 +08:00
Rex Wang
cc6c5d45b7 Fix(UI) UI fixes on docker container screens EE-3915 (#7500)
* EE-3915 ui fixes on docker container pages

* Update createcontainer.html

Update label
2022-08-17 23:37:35 +08:00
Chaim Lev-Ari
f480e0ccf6 fix(containers): make table wider [EE-3944] (#7487) 2022-08-17 12:49:29 +03:00
Ali
d85149e328 fix(kubeconfig): update button and modal styles (#7481)
EE-3947
2022-08-17 20:01:04 +12:00
Chaim Lev-Ari
cee241e77c fix(k8s/apps): show horizontal scrollbar [EE-3941] (#7472) 2022-08-16 20:59:01 +03:00
Chaim Lev-Ari
8ec9515225 fix(activity): fix angularjs error [EE-3968] (#7482) 2022-08-16 18:08:48 +03:00
Chaim Lev-Ari
d4ffaaef2f fix(activity): fix angularjs error [EE-3968] (#7483) 2022-08-16 18:08:21 +03:00
Chaim Lev-Ari
eda8347091 fix(ui/header): change font sizes [EE-3966] (#7485) 2022-08-16 18:08:08 +03:00
matias-portainer
4c23513a41 fix(home): remove edge devices from homepage list EE-3919 (#7471) 2022-08-16 09:57:55 -03:00
Matt Hook
81d1f35bdc fix snapshot url parsing issue for ip addresses (#7478) 2022-08-16 10:36:12 +12:00
Ali
36c93c7f57 fix(ui): kubernetes-consistent-styling EE-3820 (#7425) 2022-08-13 00:22:45 +06:00
Rex Wang
b67f404d8d EE-3905 changes for item 1,2,3,4,9,10,12,13,14 (#7467) 2022-08-12 12:47:44 +08:00
Chaim Lev-Ari
95fb5a4baa fix(ui): fix ui bugs [EE-3847] (#7453) 2022-08-12 15:47:56 +12:00
matias-portainer
dd372637cb feat(ui): renovate the edge devices waiting room (#7456) 2022-08-12 15:01:31 +12:00
Chaim Lev-Ari
c1a4856e9d feat(ui/datatables): add styles for nested tables [EE-3687] (#7440)
* feat(ui/datatables): add styles for nested tables
2022-08-12 14:56:48 +12:00
Chaim Lev-Ari
92b7e64689 feat(ui/sidebar): support custom logos [EE-3753] (#7436)
* feat(ui/sidebar): show right logos
2022-08-12 13:27:30 +12:00
matias-portainer
a750259a2c fix(edge): generate new EdgeID only if not present (#7454) 2022-08-11 22:23:13 -03:00
matias-portainer
87accfce5d fix(edge): parse agent platform on every polling request to avoid endpoint misconfiguration (#7452) 2022-08-11 22:21:56 -03:00
Chaim Lev-Ari
29f0daa7ea fix(edge/stacks): show correct status for env [EE-3374] (#7466) 2022-08-11 22:20:36 -03:00
Richard Wei
a247db7e93 feat(ui): added teaser styling for CE EE-3780 (#7323)
* added teaser styling for CE
2022-08-12 12:03:30 +12:00
itsconquest
1fbaf5fcbf fix(style): correct common pages [EE-3886] (#7449)
* fix(css): correct common pages [EE-3886]
2022-08-12 11:58:31 +12:00
Chaim Lev-Ari
c981e6ff7b fix(home): clear all filters [EE-3912] (#7465) 2022-08-12 02:00:33 +03:00
Richard Wei
ee1ee633d7 feat(ui): portainer wizard ui change for ce EE-3576 (#7405)
* ui change for wizard
2022-08-12 08:43:01 +12:00
Ali
a7ab0a5662 feat(ui): box-selector-style-updates EE-3698 (#7382) 2022-08-11 14:13:11 +06:00
Chaim Lev-Ari
bed4257194 refactor(containers): migrate view to react [EE-2212] (#6577)
Co-authored-by: LP B <xAt0mZ@users.noreply.github.com>
2022-08-11 07:33:29 +03:00
Chaim Lev-Ari
5ee570e075 feat(home): filter by connection type and agent version [EE-3373] (#7085) 2022-08-11 07:32:12 +03:00
Rex Wang
9666c21b8a EE-3860 fix typo (#7447) 2022-08-11 07:40:26 +08:00
Oscar Zhou
5cf789a8e4 fix(yarn): update yarn lock file to fix nightly code scan failure (#7460) 2022-08-11 10:28:58 +12:00
Matt Hook
6a4a353b92 feat(environment): update wording when editing agent environment [EE-3081] (#7445)
* change wording when editing agent environment
2022-08-11 09:27:35 +12:00
Prabhat Khera
02355acfa8 fix(ui): namespace name sort EE-3863 (#7442) 2022-08-11 09:25:29 +12:00
congs
04eb718f88 fix(gpu) EE-3191 fix gpu bugs (#7451) 2022-08-11 09:05:27 +12:00
congs
36888b5ad4 feat(ui): EE-3567 css portainer settings auth (#7423) 2022-08-10 17:49:43 +12:00
Ali
7bd971f838 fix(toast): update styles and custom button (#7450)
EE-3829
2022-08-10 17:07:35 +12:00
Chaim Lev-Ari
c3ce4d8b53 feat(sidebar): add dark theme colors [EE-3666] (#7414) 2022-08-10 07:12:20 +03:00
Ali
fb3a31a4fd feat(login): allow-show-hide-password-in-login-screen EE-3885 (#7433)
* feat(login): allow show/hide password EE-3885
2022-08-10 16:07:24 +12:00
Rex Wang
b6852b5e30 fix(UI) registry page improvement EE-2705 (#7424)
* EE-2705 bug fix

* EE-2705 hide auth switch for gitlab
2022-08-10 09:07:20 +08:00
Richard Wei
34e2178752 feat(ui): portainer registry boxselector icon ce EE-3848 (#7419)
* add icon to registry boxselector
2022-08-10 12:21:17 +12:00
fhanportainer
83a17de1c0 feat(roles): fixed search box in the Roles page. (#7448)
* feat(roles): fixed search box in the Roles page.

* feat(roles): fixed icon position
2022-08-09 19:22:10 +12:00
Oscar Zhou
e5b27d7a57 fix(ldap/tls): allow to upload tls ca certificate [EE-3654] (#7340) 2022-08-09 17:19:32 +12:00
Richard Wei
fb14a85483 fix search box for group (#7446) 2022-08-09 12:43:37 +12:00
Zhang Hao
8d4cb5e16b fix(container): some style bug on container create page [EE-3744] (#7415)
* fix(container): some style bug on container create page [EE-3744]
2022-08-09 07:50:18 +08:00
Richard Wei
ad8b8399c4 fix(ui): remove right label for switch toggle EE-3786 (#7349)
* remove right label for switch toggle
2022-08-08 16:43:31 +12:00
Dakota Walsh
8ff2fa66b6 fix(kube): update kubectl agent install instructions (#7421) 2022-08-08 14:06:10 +12:00
Zhang Hao
539948b5a6 fix(container): fixed add value and remove value for env [EE-3839] (#7429)
* fix(style): UI task issues [EE-3839]
2022-08-05 15:45:21 +08:00
Dmitry Salakhov
bfe1cace77 fix: correctly flagged pull latest image feature as limited (#7428) 2022-08-05 16:05:49 +12:00
Dakota Walsh
7b806cf586 fix(cache): trigger page reload on logout (#7407)
When portainer is restarted the user's session is invalidated and as
soon as they start clicking around they will be logged out. This PR does
two additional things when this happens.

1) We trigger a browser page reload which will force the client the grab
   the latest version of our js, css, etc. Previously if a user updated
   portainer, but never clicked the browser's refresh button they would
   never see new css changes.

2) We also set "cache-control no-cache" on the index.html header. Since
   portainer is an SPA and the the index.html is very small it makes
   sense to avoid letting the browser cache it so that the user is
   always given the latest version when the above reload is triggered.
2022-08-05 15:38:45 +12:00
Rex Wang
69a824c25b Fix(UI) Update UI of docker dashboard EE-3845 (#7422)
* EE-3846 fix alignment of left-hand side of fields
2022-08-05 10:17:31 +08:00
itsconquest
8d733ccc8c style(init): style init views [EE-3556] (#7384)
* style(init): style init views [EE-3556]

* update icons, alignment & allow clicking feature indicator link

* update cursor style on hover
2022-08-05 11:48:08 +12:00
Rex Wang
2574f223b4 fix(UI) check image name when build image EE-3010 (#7409)
* EE-3010 check image name when build image
2022-08-05 07:04:26 +08:00
fhanportainer
f2d93654f5 feat(roles): updated roles view css UI (#7389)
* feat(roles): updated roles view css UI

* feat(roles): updated icons
2022-08-05 10:26:33 +12:00
fhanportainer
5e74b90780 feat(teams): updated teams edit css UI (#7403)
* feat(teams): updated teams edit css UI

* feat(team): removed inline style.
2022-08-05 10:25:29 +12:00
fhanportainer
78ce176268 feat(teams): teams page css UI update. (#7402)
* feat(teams): teams page css UI update.

* feat(teams): added `required` attr to team name field

* feat(teams): fixed remove and search bar position

* feat(teams): fixed CreateTeamForm unit test
2022-08-05 10:24:19 +12:00
Matt Hook
dfb398d091 import the search feature (#7426) 2022-08-05 10:21:26 +12:00
Matt Hook
4e9b3a8940 fix(endpoint handler): fix endpoint address(url) parsing EE-3081] (#7408)
fix address validation when creating agent endpoint
2022-08-05 09:30:54 +12:00
Zhang Hao
0141e55936 fix(style): UI task issues [EE-3839] (#7406) 2022-08-04 23:41:12 +08:00
Rex Wang
46fba176f0 EE-3846 fix alignment of left-hand side of fields (#7413) 2022-08-04 22:05:27 +08:00
andres-portainer
441e265c32 feat(ui): renovate the docker images edit page EE-3505 (#7375) 2022-08-04 10:10:13 -03:00
Dakota Walsh
d28030abea feat(ui): namespace details UI improvements EE-3480 (#7335) 2022-08-04 14:45:44 +12:00
Dakota Walsh
aa0f1221de feat(ui): namespace access EE-3478 (#7398) 2022-08-04 13:29:55 +12:00
Richard Wei
305a949692 feat(ui): ui change for auth and activity logs EE-3798 (#7364)
* added style to authentication and activity logs
2022-08-04 11:48:05 +12:00
Richard Wei
31d3fd730c fix(ui): fix users teams missing from menu for teamlead EE-3761 (#7381)
* fix users & teams missing from menu for teamlead
2022-08-04 09:23:38 +12:00
congs
a46002502f feat(ui): EE-3719 css portainer environments access (#7359) 2022-08-04 09:05:33 +12:00
wheresolivia
56fcc91e30 fix(data-cy): rename the duplicated data-cy attribute in kube data table pages [EE-3749] (#7416)
rename the duplicated data-cy attributes in kube stack and port data table page
2022-08-04 08:28:37 +12:00
wheresolivia
8a8058e4eb fix(data-cy): rename the duplicated data-cy attribute in kube stack data table page [EE-3749] (#7411)
rename the duplicated data-cy attribute in kube stack and port data table page
2022-08-04 06:27:28 +12:00
andres-portainer
20a66fb10f fix(endpoints): remove global map to avoid panic writes EE-3838 (#7404) 2022-08-03 12:18:33 -03:00
Ali
628f822025 fix(stacks): enforce stack permissions for non admin users EE-3683 (#7399)
* fix(stacks): hide stacks in sidebar EE-3683

* fix(stacks): for unauth, take the user to the dashboard

* fix(stacks): block the user from stack details EE-3683

* fix(stacks): disable stack managment for non admins
2022-08-03 22:19:27 +12:00
Rex Wang
d8db8718bd EE-3831 Replace sort icon and search icon in all docker pages (#7400) 2022-08-03 17:43:29 +08:00
congs
5b40c79ea3 feat(ui): EE-3801 css portainer groups new (#7362) 2022-08-03 17:16:49 +12:00
Ali
ae9025c1fb feat(ui): kubernetes-volumes-list EE-3484 (#7290)
* feat(ui) volumes datatable styling EE-3484

* feat(ui): storage datatable styling EE-3484
2022-08-03 15:53:59 +12:00
fhanportainer
0014e39b61 feat(user): updated user edit view css UI (#7373) 2022-08-03 13:24:42 +12:00
Zhang Hao
5d1ea8ceb2 feat(secret&icon): secret creation page and some other icons [EE-3510] (#7357) 2022-08-03 08:56:29 +08:00
Matt Hook
079478f191 restyle (#7350) 2022-08-03 12:05:16 +12:00
Richard Wei
65c050dc87 feat(ui): ui change for edge compute settings EE-3800 (#7365)
* added style to edge compute under settings
2022-08-03 11:15:00 +12:00
congs
21fbd37bfb feat(ui): EE-3718 css portainer environments edit (#7318) 2022-08-03 10:19:28 +12:00
matias-portainer
b28f635fb2 feat(ui): renovate the edge jobs edit page EE-3531 (#7192) 2022-08-02 10:28:27 -03:00
matias-portainer
0580d3833a feat(ui): renovate the edge jobs create page EE-3530 (#7188) 2022-08-02 10:26:58 -03:00
Prabhat Khera
bff9bb7800 feature(ui): registry access screen EE-3770 (#7332) 2022-08-02 15:32:22 +12:00
Prabhat Khera
fb3d333453 fix(registries): Cannot read properties of null error on change of namespace EE-3747 (#7363) 2022-08-02 14:39:53 +12:00
congs
2c25e1d48e feat(ui): EE-3571 css portainer tags (#7383) 2022-08-02 14:22:20 +12:00
Ali
5469392ec7 feat(ui): config-details-styling EE-3472 (#7367)
* feat(ui): config details EE-3472
2022-08-02 14:21:14 +12:00
Prabhat Khera
e1c7079c81 feat(ui): ui improvements create template EE-3628 (#7352) 2022-08-02 14:10:39 +12:00
Richard Wei
75c1b485ab feat(ui): css tidy up for ui change EE-3795 (#7354)
* css tidy up for ui change
2022-08-02 12:17:22 +12:00
congs
03590d46e6 feat(ui): EE-3767 css portainer groups (#7360) 2022-08-02 11:19:57 +12:00
andres-portainer
9dc6aa81cb feat(ui): renovate the edge group creation page EE-3527 (#7191) 2022-08-01 18:24:05 -03:00
andres-portainer
d0b88d7e2f feat(ui): renovate the edge stacks edition page EE-3534 (#7213) 2022-08-01 17:48:41 -03:00
andres-portainer
5343b965aa feat(ui): renovate the docker images import page EE-3504 (#7374) 2022-08-01 17:19:07 -03:00
andres-portainer
104c82c54e feat(ui): renovate the edge groups list page EE-3529 (#7186) 2022-08-01 17:11:09 -03:00
andres-portainer
c0569a0752 feat(ui): renovate the Docker volume edit page EE-3515 (#7379) 2022-08-01 17:09:11 -03:00
matias-portainer
ad86b6b11f feat(ui): renovate the edge stacks creation page EE-3533 (#7319) 2022-08-01 15:33:18 -03:00
andres-portainer
ff32e87b97 feat(ui): renovate the Docker volume creation page EE-3514 (#7380) 2022-08-01 14:46:09 -03:00
andres-portainer
1e78234f04 feat(ui): renovate the Docker volume list page EE-3513 (#7377) 2022-08-01 14:44:44 -03:00
Zhang Hao
d0a9c046b3 refactor(docker/stack): stack creation page [EE-3486] (#7316)
* reactor(docker/stack): stack creation page [EE-3486]

* feat(stack): some missing component on stack create page and edit page [EE-3486]
2022-08-01 23:07:41 +08:00
Zhang Hao
c54bb255ba feat(container): container detail page as well as some icon changes [EE-3493] (#7361) 2022-08-01 23:06:39 +08:00
matias-portainer
8843b7b0e8 feat(ui): renovate the docker images build page EE-3503 (#7387) 2022-08-01 10:51:20 -03:00
Rex Wang
a95d734c34 EE-3487 update ui of docker/configs (#7370) 2022-08-01 20:31:56 +08:00
Rex Wang
8262487401 fix(UI) update ui of swarm/node/item EE-3518 (#7392)
* EE-3502 update page docker/host/browse and docker/volume/browse

* EE-3518 update ui of swarm/node/item
2022-08-01 16:14:43 +08:00
Ali
57e53d1a21 feat(ui): ui-improvements-helm EE-3476 (#7344)
* feat(ui): helm views ui update EE-3476
2022-08-01 19:13:58 +12:00
Rex Wang
e28a1491d4 EE-3499 update UI endpoint/settings (#7385) 2022-08-01 14:44:02 +08:00
Rex Wang
9342ba9792 EE-3502 update page docker/host/browse and docker/volume/browse (#7388) 2022-08-01 14:13:58 +08:00
Matt Hook
2552eb5e25 feat(kube): create namespace from form view [EE-3479] (#7260)
Restyle create namespace from form view
2022-08-01 16:45:28 +12:00
Matt Hook
ddaf9dc885 feat(kube): create namespace from manifest view [EE-3479] (#7306)
Restyle create from manifest
2022-08-01 16:44:56 +12:00
Richard Wei
11c778cfeb import react2angular for used by icon, tooltip and tableheader (#7391) 2022-08-01 14:59:00 +12:00
Ali
11dffdee9a feat(ui): update dashboard table & items EE-3474 (#7351) 2022-08-01 13:29:49 +12:00
Richard Wei
d4d80ed8f7 feat(ui): ui change for create access token EE-3541 (#7366)
* ui change for create access token
2022-08-01 10:08:45 +12:00
Zhang Hao
0ba10b44ec feat(secret): secret item page [EE-3511] (#7356) 2022-07-31 20:15:12 +08:00
Richard Wei
0f617f7f87 fix js console error for access control and stack page (#7347) 2022-07-29 19:11:03 +12:00
Richard Wei
423dd5e394 feat(ui): portainer new ui for homepage EE-3554 (#7328)
* add icon to homepage
2022-07-29 16:13:02 +12:00
congs
44737029a9 fix(gpu): EE-3743 gpus null error (#7342) 2022-07-29 16:08:17 +12:00
Prabhat Khera
ce22544c60 feature(ui): UI security constraints screen EE-3706 (#7314) 2022-07-29 14:41:33 +12:00
Matt Hook
9106e74e61 restyle the web editor (#7333) 2022-07-29 12:54:17 +12:00
fhanportainer
6c57ddb563 feat(ui): EE-3574 css portainer users (#7295)
* feat(ui): EE-3574 css portainer users

* feat(users): updated UI based on PR feedback

* feat(user): updated admin toggle with <por-switch-field>

* feat(user): fixed alert circle position
2022-07-29 12:45:37 +12:00
Dakota Walsh
a2e1570162 feat(ui): volume detals UI improvements EE-3483 (#7329) 2022-07-29 11:43:37 +12:00
Chaim Lev-Ari
ea60740d48 fix(sidebar): save sidebar state to local storage (#7207) 2022-07-28 14:24:25 -03:00
Chaim Lev-Ari
762c664948 feat(edge): create edge device with wizard [EE-3096] (#7029) 2022-07-28 10:34:22 -03:00
Ali
d574a71cb1 feat(ui): allow-different-modal-icons EE-3751 (#7299)
* feat(ui): update modal icons EE-3751
2022-07-28 17:33:21 +12:00
Prabhat Khera
bb066cd58c fix(ui): certificate fields fixed EE-3692 (#7336) 2022-07-28 14:41:26 +12:00
Prabhat Khera
e779939ae1 feature(ui): ui improvements kube config add from EE-3471 (#7341) 2022-07-28 11:17:32 +12:00
Richard Wei
aa830a0e58 fix(ui): fix docker images page error on pageheader EE-3668 (#7212)
* fix docker images page error with link on page-header
2022-07-28 09:53:19 +12:00
matias-portainer
52ac54f15c feat(ui): renovate edge devices list page EE-3622 (#7210) 2022-07-27 17:09:44 -03:00
matias-portainer
cc0ab75aca feat(ui): renovate the edge devices create page EE-3620 (#7221) 2022-07-27 11:19:23 -03:00
matias-portainer
7e3347da2b feat(ui): renovate the FDO devices list EE-3669 (#7231) 2022-07-27 10:47:38 -03:00
matias-portainer
87e9d7f8d4 feat(ssl): use ECDSA instead of RSA to generate the self-signed certificates EE-3097 (#6891) 2022-07-27 10:46:21 -03:00
Rex Wang
6d3a33635d EE-3694 update UI of docker/custom template (#7345) 2022-07-27 21:04:31 +08:00
Rex Wang
090268d7b6 EE-3485 update ui of docker template (#7339) 2022-07-27 20:23:33 +08:00
Rex Wang
698a91596e EE-3498 update registry/endpoint registry/manage access (#7353) 2022-07-27 20:22:40 +08:00
Ali
bb447bb02a fix(ui): remove unwanted icon hover fill EE-3737 (#7284) 2022-07-27 14:11:54 +12:00
Zhang Hao
5ffcbe8677 refactor(service): docker service edition page [EE-3520] (#7327) 2022-07-27 09:55:16 +08:00
Richard Wei
ac6296b86d feat(ui): portainer settings page ui EE-3566 (#7259)
* settings page ui change
2022-07-27 13:05:25 +12:00
Zhang Hao
3239a61bda refactor(container): container creation page and plus button [EE-3744] (#7325) 2022-07-27 07:20:21 +08:00
Zhang Hao
2a43285593 feat(docker/component/button-selector): change button selector style and remove button style [EE-3491] (#7315) 2022-07-27 07:18:06 +08:00
Richard Wei
36071837cb feat(ui): portainer login page ui EE-3542 (#7244)
* ui change for login page

* Update app/portainer/views/auth/auth.html

Co-authored-by: itsconquest <william.conquest@portainer.io>

* remove inline styles logout view

Co-authored-by: itsconquest <william.conquest@portainer.io>
2022-07-27 11:02:41 +12:00
Ali
1ef713d80b feat(ui): custom template item EE-3738 (#7303) 2022-07-27 09:40:22 +12:00
Chaim Lev-Ari
82b848af0c refactor(azure): migrate module to react [EE-2782] (#6689)
* refactor(azure): migrate module to react [EE-2782]

* fix(azure): remove optional chain

* feat(azure): apply new icons in dashboard

* feat(azure): apply new icons in dashboard

* feat(ui): allow single string for breadcrumbs

* refactor(azure/containers): use Table.content

* feat(azure/containers): implement new ui [EE-3538]

* fix(azure/containers): use correct icon

* chore(tests): mock svg as component

* fix(azure): fix tests

Co-authored-by: matias.spinarolli <matias.spinarolli@portainer.io>
2022-07-26 16:44:08 -03:00
LP B
b059641c80 fix(app/environment): console errors related to usage of React components [EE-3760] (#7310) 2022-07-26 17:50:49 +02:00
matias-portainer
728e885b9d fix(edge): restore search bar to app templates page EE-2522 (#7313) 2022-07-26 11:04:56 -03:00
congs
3acefba069 feat(ui): EE-3540 portainer-account (#7177) 2022-07-26 17:17:54 +12:00
Ali
9205f67791 feat(ui): kubernetes-configurations-list EE-3470 (#7285)
* feat(ui): configmaps/secrets table EE-3470

* feat(ui): conditionally show parent EE-3470
2022-07-26 17:12:02 +12:00
Zhang Hao
6d95643a68 refactor(service): docker service creation page [EE-3519] (#7326) 2022-07-26 07:04:01 +08:00
congs
149c414d08 fix(permission): EE-3772 Team leaders are able to see all environments (#7331) 2022-07-26 11:02:25 +12:00
matias-portainer
f8b4663e0a feat(ui): renovate the edge jobs list page EE-3532 (#7187) 2022-07-25 13:28:58 -03:00
Chaim Lev-Ari
7b774c702d fix(app): add style for be-indicator (#7140) 2022-07-25 13:24:54 -03:00
andres-portainer
8045a15a50 feat(ui): renovate the edge stacks list page EE-3535 (#7189) 2022-07-25 13:14:15 -03:00
Richard Wei
9a18dd8162 fix console error for feather icon (#7305) 2022-07-25 20:11:48 +12:00
Richard Wei
70a7eefa22 fixed cloud in ce menu (#7334) 2022-07-25 15:23:09 +12:00
Rex Wang
3356d1abe2 fix(UI) Update docker container inspect,log,stats,console,attach pages EE-3492 (#7307)
* EE-3492 update docker container inspect,log,stats,console,attach pages

* EE-3492 bug fixing

* EE-3492 replace chart bar icon

* EE-3492 bug fix

* Update resourcePoolsDatatable.html

* Update resourcePoolsDatatable.html
2022-07-25 11:03:22 +08:00
Richard Wei
7ee8dac832 fix tooltip issue for ce (#7281)
fix tooltip issue for ce
2022-07-25 13:20:36 +12:00
Rex Wang
5b3f099f4e fix(UI) Update all network pages EE-3509 (#7324)
* EE-3509 update all network pages

* EE-3509 update access control panel and network container table
2022-07-25 07:57:18 +08:00
congs
5f5cb36df1 feat(ui): EE-3553 css-portainer-environmnets (#7193) 2022-07-25 10:39:15 +12:00
Prabhat Khera
3645ff7459 feat(ui): cluster setup page done EE-3705 (#7267) 2022-07-22 14:16:50 +12:00
Chaim Lev-Ari
9a92b97b7e fix(sidebar): show authorized links [EE-3610] (#7152) 2022-07-22 14:14:31 +12:00
matias-portainer
005c48b1ad fix(edgejobs): validate date when saving job in basic configuration (#7048) 2022-07-21 16:43:52 -03:00
itsconquest
4fb1880ddc fix(auth): correctly calculate LDAP teamsync [EE-3704] (#7293) 2022-07-21 21:29:34 +12:00
Prabhat Khera
54145ce949 fix(kubeconfig): fix kubeconfig url EE-3455 (#7282) 2022-07-21 16:59:40 +12:00
itsconquest
b040aa1e78 fix(TLS): remove file type validation [EE-3672] (#7280) 2022-07-21 16:25:23 +12:00
congs
985eef6987 feat(ui): update registries css (#7249) [EE-3562] 2022-07-21 12:28:58 +12:00
Ali
a5c3116b0c fix(build): remove build script EE-3481 (#7300) 2022-07-21 10:09:56 +12:00
matias-portainer
df381b6a33 feat(templates): remove toggle and add sorting for app templates EE-2522 (#6884) 2022-07-20 16:27:15 -03:00
Chao Geng
9223c0226a EE-3742 update tool bar / action bar / search bar / pagination (#7298)
Co-authored-by: RexWangPT <rex.wang@portainer.io>
2022-07-21 00:31:13 +08:00
Ali
314fdc850e feat(ui): kubernetes-namespace-list EE-3481 (#7276)
* feat(ui): namespace list view ui changes EE-3481
2022-07-20 15:52:00 +12:00
Zhang Hao
43bbeed141 refactor(docker/switch/component): implement new design [EE-3688] (#7239)
* refactor(docker/switch/component): implement new design [EE=3688]

* revert create volume

* revert por-switch on exec.html

* refactor(container): switch fields on container creation page and edition page

* refactor(container): switch fields on networking/secret/servicewebhook/swarmvisual

* bug fixed

* code review issues

* merge code

* fix teaser for container edition

* fix encode secret toggle bug on adding secret page

* fixed a bug for service webhook toggle
2022-07-20 08:39:44 +08:00
wheresolivia
e07253bcef fix kube namespace memory usage data-cy (#7294) 2022-07-20 10:50:54 +12:00
Matt Hook
23b9baa059 feat(icons): add more svg icons and other tweaks [EE-3721] (#7270) 2022-07-20 10:50:30 +12:00
Chaim Lev-Ari
05357ecce5 fix(edge): filtering of edge devices [EE-3210] (#7077)
* fix(edge): filtering of edge devices [EE-3210]

fixes [EE-3210]

changes:
- replaces `edgeDeviceFilter` with two filters:
	- `edgeDevice`
	- `edgeDeviceUntrusted`

these filters will only apply to the edge endpoints in the query (so it's possible to get both regular endpoints and edge devices).

if `edgeDevice` is true, will filter out edge agents which are not an edge device.
			false, will filter out edge devices

`edgeDeviceUntrusted` applies only when `edgeDevice` is true. then false (default) will hide the untrusted edge devices, true will show only untrusted edge devices.

fix(edge/job-create): retrieve only trusted endpoints + fix endpoint selector pagination limits onChange

fix(endpoint-groups): remove listing of untrusted edge envs (aka in waiting room)

refactor(endpoints): move filter to another function

feat(endpoints): separate edge filters

refactor(environments): change getEnv api

refactor(endpoints): use single getEnv

feat(groups): show error when failed loading envs

style(endpoints): remove unused endpointsByGroup

* chore(deps): update go to 1.18

* fix(endpoint): filter out untrusted by default

* fix(edge): show correct endpoints

* style(endpoints): fix typo

* fix(endpoints): fix swagger

* fix(admin): use new getEnv function

Co-authored-by: LP B <xAt0mZ@users.noreply.github.com>
2022-07-19 18:00:45 +02:00
LP B
1a8fe82821 fix(app/mustache): reuse mustache variables in templates [EE-3689] (#7286) 2022-07-19 15:38:00 +02:00
LP B
95f4db4f48 fix(app/templates): handle special characters in mustache templates [EE-3708] (#7288) 2022-07-19 14:05:18 +02:00
Rex Wang
43600083a7 EE-3723 update headers to feather icon (#7275) 2022-07-19 11:29:50 +08:00
Dmitry Salakhov
e6477b0b97 fix(users): admin can change password with any auth method (#7268) [EE-3671] 2022-07-19 11:26:34 +12:00
Prabhat Khera
6aa7fdb4f2 feat(ui): UI improvements node details screen EE-3468 (#7256) 2022-07-18 11:48:24 +12:00
congs
4997e9c7be feat(gpu) EE-3191 Add GPU support for containers (#7146) 2022-07-18 11:02:14 +12:00
Peter Maguire
f0456cbf5f fix(containers): fix incorrect grammar on recreate tooltip (#7236) 2022-07-15 17:04:41 +12:00
itsconquest
a0d349e0b3 feat(buildinfo): ability to see build info [EE-2552] (#7107)
* feat(buildinfo): ability to see build info [EE-2252]

* handle dark theme

* feat: add build info to status version

* feat: include ldflags in azure pipeline

* echo shell commands in azure build

* clean up main log

* allow tests to pass

* use data from backend

* allow clicking off modal to dismiss

* add placeholder versions

* refactor

* update button class

* fix modal displaying behind elements

Co-authored-by: Dmitry Salakhov <to@dimasalakhov.com>
2022-07-15 11:09:38 +12:00
Prabhat Khera
f5e774c89d feat(ui): UI improvements kube app detail EE-3463 (#7176) 2022-07-15 10:49:12 +12:00
Oscar Zhou
552d3f8a3e fix(setting): update the por switch field component property (#7257) 2022-07-15 08:27:43 +12:00
Chao Geng
39f9173956 Fix(ui):docker-templates-ui [EE-3485] (#7170)
* EE-3485 update ui

* EE-3485 update ui

* EE-3485 use feather icon to replace trash icon

Co-authored-by: RexWangPT <rex.wang@portainer.io>
2022-07-14 23:16:59 +08:00
Rex Wang
e4fc41fc94 fix(ui): update UI of docker/network/create EE-3507 (#7255)
* EE-3507 update UI of docker/network/create

* EE-3507 update all icons
2022-07-14 21:35:37 +08:00
Prabhat Khera
ce7d234cba feat(ui): ui improvements on cluster landing page EE-3467 (#7245) 2022-07-14 13:50:23 +12:00
Prabhat Khera
35701f5899 svg support in icons.tsx (#7266) 2022-07-14 13:44:56 +12:00
Matt Hook
3d4d2b50ae update wording, docker-compose to docker compose (#7233) 2022-07-14 10:40:34 +12:00
Ali
0da4e3ae63 feat(ui): kube app list ui styling EE-3464 (#7247)
* feat(ui): apply app datatable changes EE-3464
2022-07-13 21:21:26 +12:00
Richard Wei
ad7055ee01 feat(ui): turn off all teaser toggle in CE (#7227)
* turn off all teaser toggle in CE
2022-07-13 15:15:11 +12:00
Richard Wei
8076455423 added nested blue icon to widget header title (#7250)
*added nested blue icon to widget header title
2022-07-13 08:48:48 +12:00
Ali
23eca3ce80 fix(r2a): fix wizard r2a bug EE-3680 (#7241) 2022-07-12 13:15:14 +12:00
sunportainer
4cc672f902 fix(UI): update-log-viewer-ui [EE-3522] (#7202)
* fix update log viewer layout

* use por-switch in logs

Co-authored-by: Hao Zhang <hao.zhang@portainer.io>
2022-07-12 07:26:23 +08:00
Prabhat Khera
82fb5f7ac1 feat(kubernetes): UI improvements kube app create EE-3462 (#7149) 2022-07-11 14:05:23 +12:00
fhanportainer
de59ea030a feat(stack): added ui label in env var section (#7010)
* feat(stack): added ui label in env var section

* feat(stack): added ui label in env var advanced section

* feat(stack): added showHelpMessage flag

* feat(stack): show help message when stack created from web editor.
2022-07-10 00:01:51 +12:00
Matt Hook
d9be6d1724 downloaded compose file should now be called docker-compose (#7228) 2022-07-08 21:47:50 +12:00
Dakota Walsh
958a8e97e9 fix(migration): close the database before running backups EE-3627 (#7218)
* fix(migration): close the database before running backups

On certain filesystems, particuarly NTFS when a network mounted windows
file server is used to store portainer's database, you are unable to
copy the database while it is open. To fix this we simply close the
database and then re-open it after a backup.

* handle close and open errors

* dont return error on nil
2022-07-08 21:05:04 +12:00
Matt Hook
5fd202d629 update to latest compose wrapper lib (#7226) 2022-07-08 16:02:24 +12:00
LP B
768f1aa663 fix(k8s/app-templates): display moustache variables fields when deploying from app template (#7184) 2022-07-08 14:15:23 +12:00
Ali
69caa1179f fix(ui): stacks example feedback EE-3676 (#7225) 2022-07-08 13:25:39 +12:00
Richard Wei
9a2cdc4a93 feat(ui): replace boxselector with react component EE-3593 (#7215)
* replace boxselector and upload vendor icon
2022-07-08 12:57:36 +12:00
Ali
14a8b1d897 feat(ui): add sorting icon component and table header cell styling EE-3626 (#7165)
* feat(ui): add sorting icons EE-3626

feat(ui): Add react component for sorting icons

feat(ui) make component usable in angular 

* feat(ui): update angular example EE-3626
2022-07-08 01:20:33 +12:00
Richard Wei
712207e69f fix(ui): fix tooltip background color (#7211) 2022-07-07 13:31:55 +03:00
Chaim Lev-Ari
8d46692d66 refactor(ui): move datatable css from bootstrap-override [EE-3664] (#7206) 2022-07-07 07:29:46 +03:00
Oscar Zhou
3241738775 fix(gitops): show prune option only in the swarm stack (#7190) 2022-07-07 11:23:22 +12:00
Chaim Lev-Ari
ce840997bf feat(ui): sort search bar icon [EE-3663] (#7205) 2022-07-06 17:05:17 +03:00
Chaim Lev-Ari
88c4a43a19 feat(ui): add icon to button [EE-3662] (#7204) 2022-07-06 17:05:00 +03:00
Chao Geng
b4acbfc9e1 fix(registry): Add input prompt and checker in edit page [EE-2705] (#7106)
* EE-2705 restrict registry edit options for different registry type
2022-07-06 19:11:59 +08:00
Chaim Lev-Ari
8bf1c91bc9 refactor(app): redesign dashboard-item component [EE-3634] (#7175) 2022-07-06 11:23:53 +03:00
Richard Wei
a66fd78dc1 feat(ui): apply react pageheader to all pageview EE-3615 (#7178)
Co-authored-by: Chaim Lev-Ari <chiptus@gmail.com>
2022-07-06 09:08:45 +03:00
Chaim Lev-Ari
b004b33935 fix(sidebar): sort issues [EE-3447] (#7147) 2022-07-06 08:09:14 +03:00
Dmitry Salakhov
d32793e84e fix(users): enable manual user addition (#7198) 2022-07-06 15:47:11 +12:00
Dmitry Salakhov
fd4b515350 fix(oauth): analyze id_token for Azure [EE-2984] (#7000) 2022-07-06 13:22:57 +12:00
Chaim Lev-Ari
0cd2a4558b chore(app): fix e2e tests (#7154) 2022-07-04 12:20:46 +03:00
congs
89359a21ce fix(docker): EE-3247 Portainer should not send a host information request when host management features are disabled (#7038) 2022-07-04 17:13:15 +12:00
Richard Wei
69baa279d4 feat(ui): break css into module EE-3629 (#7180)
* break css into module and fix icon mode
2022-07-04 14:11:13 +12:00
Dmitry Salakhov
33861a834b fix(compose): merge default and in-place stack env vars [EE-2860] (#7076) 2022-07-04 13:16:04 +12:00
Matt Hook
dd4d126934 feat(switch): add optional switch text [EE-3625] (#7164)
* add optional switch text
2022-07-04 13:05:04 +12:00
Oscar Zhou
7275d23e4b feat(stack/swarm): add prune option for swarm stack redeployment [EE-2678] (#7025) 2022-07-04 11:39:03 +12:00
Chaim Lev-Ari
d7306fb22e refactor(app): replace angularjs tooltip with react [EE-3606] (#7172)
* refactor(app): replace angularjs tooltip with react
2022-07-04 11:21:25 +12:00
Dmitry Salakhov
ebc0a8c772 fix: update build scripts for mac (#7104) 2022-07-04 10:43:11 +12:00
Richard Wei
f26e1fa21b add inline-flex to button group (#7168)
* add inline-flex to button group
2022-07-04 07:16:45 +12:00
matias-portainer
6b27ba9121 fix(edge): delete endpoint proxy only when updating URL, TLS or is Edge Agent on Kubernetes EE-2759 (#7086) 2022-07-01 11:36:01 -03:00
congs
975dc9c1da fix(edge): EE-3092 hide the ability to add edge agents in Docker Desktop extension (#7090) 2022-07-01 17:22:40 +12:00
Chaim Lev-Ari
6fe26a52dd feat(app): ui additional css class [EE-3594] (#7157)
* feat(app): ui additional css class [EE-3594]
2022-07-01 13:14:22 +12:00
Chao Geng
cd66e32912 EE-2570 disable pull image toggle when invalid (#7002) 2022-06-30 08:35:32 +08:00
Prabhat Khera
81f8b88541 fix ingress published url (#7113) 2022-06-29 16:28:09 +12:00
Chaim Lev-Ari
882051cc30 chore(sidebar): add data-cys [EE-3605] (#7143)
* chore(sidebar): add data-cys [EE-3605]

fix [EE-3605]
2022-06-28 19:36:40 +03:00
Chaim Lev-Ari
ed8f9b5931 feat(sidebar): implement new design [EE-3447] (#7118) 2022-06-28 10:42:42 +03:00
Prabhat Khera
e5e57978af delete force terminating namespace (#7081) 2022-06-28 16:35:30 +12:00
Steven Kang
75fef397d3 Set static DOCKER_VERSION for ppc64le and s390x (#7136) 2022-06-28 11:40:18 +12:00
Chaim Lev-Ari
624490716e fix(environments): hide async mode on deployment [EE-3380] (#7130)
fixes [EE-3380]
2022-06-28 10:23:15 +12:00
andres-portainer
8eff32ebc7 fix(css): improve the handling of different color entries EE-3603 (#7134) 2022-06-27 18:11:14 -03:00
Chaim Lev-Ari
cd19eb036b refactor(app): use colors with tailwind [EE-3601] (#7133)
* refactor(app): use colors with tailwind
2022-06-28 07:16:28 +12:00
Chaim Lev-Ari
95f706aabe fix(analytics): load public settings [EE-3590] (#7128) 2022-06-27 19:29:17 +03:00
Ali
1551b02fde fix(home): dont close filter on select EE-3257 (#6991) 2022-06-27 13:47:07 +12:00
itsconquest
557f4773cf feat(extension): remove unused port [EE-3152] (#7075) 2022-06-27 10:27:37 +12:00
Steven Kang
b84e1c8550 Set static DOCKER_VERSION for ppc64le and s390x (#7123) 2022-06-27 09:48:49 +12:00
Chaim Lev-Ari
46e1a01625 refactor(docker): move components to react [EE-3348] (#7084) 2022-06-26 17:16:50 +03:00
Chaim Lev-Ari
7238372d8d fix(api): add missing edge types [EE-3590] (#7116) 2022-06-26 08:38:23 +03:00
andres-portainer
00126cd08a fix(wizard): replace the YAML file by the docker commands EE-3589 (#7111) 2022-06-24 14:59:10 -03:00
LP B
58c44ad1ea fix(app/account): ensure newTransition exists in uiCanExit [EE-3336] (#7110) 2022-06-24 17:35:35 +02:00
Chaim Lev-Ari
84611a90a1 refactor(sidebar): migrate sidebar to react [EE-2907] (#6725)
* refactor(sidebar): migrate sidebar to react [EE-2907]

fixes [EE-2907]

feat(sidebar): show label for help

fix(sidebar): apply changes from ddExtension

fix(sidebar): resolve conflicts

style(ts): add explanation for ddExtension

fix(sidebar): use enum for status

refactor(sidebar): rename to EdgeComputeSidebar

refactor(sidebar): removed the need of `ident` prop

style(sidebar): add ref for mobile breakpoint

refactor(app): document testing props

refactor(sidebar): use single sidebar item

refactor(sidebar): use section for nav

refactor(sidebar): rename sidebarlink to link

refactor(sidebar): memoize menu paths

fix(kubectl-shell): infinite loop on hooks dependencies

refactor(sidebar): use authorized element

feat(k8s/shell): track open shell

refactor(k8s/shell): remove memoization

refactor(settings): move settings queries to queries

fix(sidebar): close sidebar on mobile

refactor(settings): use mutation helpers

refactor(sidebar): remove memo

refactor(sidebar): rename sidebar item for storybook

refactor(sidebar): move to react

gprefactor(sidebar): remove dependence on EndProvider

feat(environments): rename settings type

feat(kube): move kubeconfig button

fix(sidebar): open submenus

fix(sidebar): open on expand

fix(sibebar): show kube shell correctly

* fix(sidebar): import from react component

* chore(tests): fix missing prop
2022-06-23 10:25:56 +03:00
Chaim Lev-Ari
f78a6568a6 feat(ui): portainer base component css change [EE-3381] (#7115) 2022-06-23 09:32:18 +03:00
Chaim Lev-Ari
825269c119 fix(edge): show heartbeat for async env [EE-3380] (#7097) 2022-06-22 20:11:46 +03:00
Chaim Lev-Ari
60cd7b5527 chore(tests): remove cypress code [EE-3580] (#7103) 2022-06-22 07:59:53 +03:00
Matt Hook
767fabe0ce fix docker download path for mac platforms (#7102) 2022-06-22 10:06:46 +12:00
matias-portainer
f86ba7b176 feat(edge): move edge jobs out of beta (#7105) 2022-06-21 17:57:59 -03:00
Hao Zhang
912250732a feat(psp): kubernetes pod security policy EE-1577 (#6553)
* docs(github): fix slack link [EE-2438] (#6541)

Co-authored-by: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Co-authored-by: cheloRydel <marcelorydel26@gmail.com>
Co-authored-by: Chao Geng <93526589+chaogeng77977@users.noreply.github.com>
Co-authored-by: chaogeng77977 <chao.geng@portainer.io>
2022-06-20 15:48:41 +08:00
itsconquest
ae731b5496 fix(auth): track skips per user [EE-3318] (#7089) 2022-06-20 17:00:07 +12:00
Chaim Lev-Ari
92eaa02156 fix(docker/networks): show correct resource control data [EE-3401] (#7060) 2022-06-17 19:21:41 +03:00
Chaim Lev-Ari
18252ab854 refactor(app): move react components to react codebase [EE-3179] (#6971) 2022-06-17 19:18:42 +03:00
itsconquest
212400c283 fix(auth): clear skips when using new instance [EE-3331] (#7027) 2022-06-17 14:45:47 +12:00
LP B
8ed41de815 fix(app/account): create access token button (#7014)
* fix(api): remove unused leftover imports

* fix(app/account): create access token button

* fix(app/formcontrol): error message overlapping input on smaller screens
2022-06-16 21:25:37 +02:00
Chaim Lev-Ari
97a880e6c1 feat(custom-templates): hide variables [EE-2602] (#7068) 2022-06-16 08:32:41 +03:00
itsconquest
f39775752d feat(auth): allow single char passwords [EE-3385] (#7050)
* feat(auth): allow single character passwords

* match weak password modal logic to slider
2022-06-16 12:31:36 +12:00
Matt Hook
6d6c70a98b fix(swarm): don't stomp on the x-registry-auth header EE-3308 (#7080)
* don't stomp on the x-registry-auth header

* del header if empty json provided for registry auth
2022-06-16 09:53:58 +12:00
Dmitry Salakhov
461fc91446 fix: clarify password change error (#7082) 2022-06-15 16:56:59 +12:00
itsconquest
8059cae8e7 fix(auth): notify user password requirements [EE-3344] (#7042)
* fix(auth): notify user password requirements [EE-3344]

* fix angular code
2022-06-15 16:01:19 +12:00
congs
41107191c3 fix(teamleader): EE-3411 normal users get an unauthorized error (#7052) 2022-06-14 14:12:25 +12:00
sunportainer
cb6a5fa41d fix(typo):UI and logs EE-3282 (#7063)
* fix logs and UI typos
2022-06-13 14:53:51 +08:00
Ali
66799a53f4 fix(wizard): return back to envs page EE-3419 (#7065) 2022-06-13 14:59:41 +12:00
congs
892fdbf60d fix(teamleader): EE-3383 allow teamleader promote member to teamleader (#7040) 2022-06-10 17:13:33 +12:00
Chao Geng
b6309682ef feat(kubeconfig): pagination for downloading kubeconfigs EE-2141 (#6895)
* EE-2141 Add pagination to kubeconfig download dialog
2022-06-10 11:42:27 +08:00
Ali
be11dfc231 fix(wizard): show teasers for kaas and kubeconfig features [EE-3316] (#7008)
* fix(wizard): add kubeconfig, nomad and kaas teasers
2022-06-10 09:17:13 +12:00
congs
12527aa820 fix(teamleader): EE-3332 hide name and leaders (#7031) 2022-06-09 14:22:35 +12:00
Matt Hook
0d0f9499eb chore(version): fix readme version (#7028) 2022-06-08 23:20:58 +12:00
Ali
60eab3e263 fix(wizard): use 'New Environments' title EE-3329 (#7034) 2022-06-08 16:35:58 +12:00
Chao Geng
eb547162e9 fix(image) add validation of image name in build image page [EE-3010] (#6988)
* EE-3010 add validation of image name
2022-06-07 16:42:09 +08:00
Matt Hook
0864c371e8 chore(version): bump develop branch version to 2.15 (#7019)
* bump version to 2.15
2022-06-07 11:00:36 +12:00
Chaim Lev-Ari
b90b1701e9 fix(users): remove unused imports [EE-3340] (#7016)
fixes [EE-3340]
2022-06-06 10:04:33 +03:00
Ali
eb4ff12744 feat(wizard): replace-the-add-envs-button-with-env-wizard-button EE-3001 (#7013)
* feat(envs): on env click, direct user to wizard
2022-06-03 22:33:17 +12:00
congs
0522032515 feat(teamleader) EE-294 redesign team leader (#6973)
feat(teamleader) EE-294 redesign team leader (#6973)
2022-06-03 16:44:42 +12:00
itsconquest
bca1c6b9cf feat(internal-auth): ability to set minimum password length [EE-3175] (#6942)
* feat(internal-auth): ability to set minimum password length [EE-3175]

* pass props to react component

* fixes + WIP slider

* fix slider updating + add styles

* remove nested ternary

* fix slider updating + add remind me later button

* add length to settings + value & onchange method

* finish my account view

* fix slider updating

* slider styles

* update style

* move slider in

* update size of slider

* allow admin to browse to authentication view

* use feather icons instead of font awesome

* feat(settings): add colors to password rules

* clean up tooltip styles

* more style changes

* styles

* fixes + use requiredLength in password field for icon logic

* simplify logic

* simplify slider logic and remove debug code

* use required length for logic to display pwd length warning

* fix slider styles

* use requiredPasswordLength to determine if password is valid

* style tooltip based on theme

* reset skips when password is changed

* misc cleanup

* reset skips when required length is changed

* fix formatting

* fix issues

* implement some suggestions

* simplify logic

* update broken test

* pick min password length from DB

* fix suggestions

* set up min password length in the DB

* fix test after migration

* fix formatting issue

* fix bug with icon

* refactored migration

* fix typo

* fixes

* fix logic

* set skips per user

* reset skips for all users on length change

Co-authored-by: Chaim Lev-Ari <chiptus@gmail.com>
Co-authored-by: Dmitry Salakhov <to@dimasalakhov.com>
2022-06-03 16:00:13 +12:00
Matt Hook
4195d93a16 fix typo 2022-06-03 14:21:55 +12:00
Matt Hook
e8a8b71daa feat(compose): upgrade to docker compose v2 EE-2096 (#6994)
Upgrade to compose v2 + new helm + new kubectl
2022-06-03 13:50:37 +12:00
Ali
aea62723c0 fix(forms): increase-click-area-for-expandable-form-section EE-3314 (#7007)
* fix(forms): increase click area for form section
2022-06-03 13:29:23 +12:00
Prabhat Khera
9b58c2e466 rename output_35 to output_24_to_latest (#7006) 2022-06-02 11:30:42 +12:00
Prabhat Khera
c41f7f8270 chore(version): version bump to 2.14.0 (#6958) 2022-06-02 10:53:48 +12:00
Chaim Lev-Ari
ac096dda46 feat(wizard): add edge form [EE-3000] (#6979) 2022-06-01 07:28:31 +03:00
Chaim Lev-Ari
e686d64011 refactor(docker): strongly type snapshot [EE-3256] (#6990)
* refactor(docker): strongly type snapshot [EE-3256]

fixes [EE-3256]

* fix(endpoints): return empty from association api

* refactor(docker): ignore raw snapshot for swagger
2022-05-31 13:03:10 +03:00
Chaim Lev-Ari
1ccdb64938 refactor(custom-templates): render template variables [EE-2602] (#6937) 2022-05-31 13:00:47 +03:00
Prabhat Khera
71c0e8e661 fix(kubernetes): fix redeploying kubernetes app EE-2875 (#6984) 2022-05-31 10:12:37 +12:00
andres-portainer
c162e180e0 fix(endpoints): remove global map to avoid panic writes EE-3160 (#6918) 2022-05-30 11:22:37 -03:00
Ali
e806f74652 refactor(tailwind): add-consistent-theme-colors-to-tailwind EE-3255 (#6989)
* refactor(tailwind): add custom colors EE-3255
2022-05-30 14:01:05 +12:00
Chaim Lev-Ari
d52417c14f refactor(app): convert tag-selector to react [EE-2983] (#6783) 2022-05-29 09:14:14 +03:00
Chaim Lev-Ari
75d854e6ad Revert "refactor(docker): strongly type snapshot [EE-3256]"
This reverts commit 0b2217a916.
2022-05-26 15:39:55 +03:00
Chaim Lev-Ari
0b2217a916 refactor(docker): strongly type snapshot [EE-3256]
fixes [EE-3256]
2022-05-26 15:34:34 +03:00
Chao Geng
ca30efeca7 EE-1892 Centralize prompt dialog (#6903) 2022-05-24 20:14:38 +08:00
Chaim Lev-Ari
dc98850489 feat(app): enforce using of props in r2a [EE-3215] (#6943) 2022-05-24 08:35:20 +03:00
Chaim Lev-Ari
01dc9066b7 refactor(wizard): migrate to react [EE-2305] (#6957) 2022-05-23 17:32:51 +03:00
Chao Geng
3aacaa7caf feat(dashboard) remove environment url from dashboard EE-2849 (#6955)
* EE-2849 remove environment url from dashboard

* EE-2849 only remove edge env's url

* EE-2849 remove logging
2022-05-23 17:05:37 +08:00
Chaim Lev-Ari
b031a30f62 feat(edge-devices): set specific page to view [EE-2082] (#6869) 2022-05-23 10:57:22 +03:00
Chaim Lev-Ari
12cddbd896 feat(demo): disable features on demo env [EE-1874] (#6040) 2022-05-22 08:34:09 +03:00
Chao Geng
3791b7a16f fix(kube): misspelling kube namespace (#6951) 2022-05-20 07:34:30 +08:00
matias-portainer
d754532ab1 chore(edgestacks): add unit tests for edge stacks (#6931)
chore(edgestacks): add unit tests for edge stacks EE-3172
2022-05-19 17:13:51 -03:00
Chao Geng
9a48ceaec1 fix(docker): Restrict registry edit options for different registry type EE-2705 (#6708)
* EE-2705 restrict registry edit options for different registry type

* EE-2705 quay and azure registry should not disable authentication

* EE-2705 Resolve conflict
2022-05-18 18:46:24 +08:00
Chaim Lev-Ari
1132c9ce87 refactor(app): create empty react structure [EE-3178] (#6926) 2022-05-17 07:22:44 +03:00
itsconquest
668d526604 fix(networks): handle windows specific system networks [EE-2594] (#6922) 2022-05-17 14:45:30 +12:00
Chaim Lev-Ari
0e257c200f chore(app): use base font-size of 16px [EE-3186] (#6938) 2022-05-16 10:24:13 +03:00
congs
df05914fac fix(git) EE-2026 git default branch (#6876)
fix(git) EE-2026 git default branch
2022-05-16 09:35:11 +12:00
Chaim Lev-Ari
0ffb84aaa6 refactor(app): add rq mutation helpers [EE-3176] (#6923) 2022-05-15 10:01:08 +03:00
Chaim Lev-Ari
b01180bb29 chore(deps): remove lodash-es dependency [EE-2560] (#6576) 2022-05-12 08:44:53 +03:00
cong meng
16f8b737f1 fix(pwd) EE-3161 ease the minimum password restrictions to 12 characters (#6921)
* fix(pwd): EE-3161 ease the minimum password restrictions to 12 characters
2022-05-12 13:17:01 +12:00
itsconquest
d9d1d6bfaa feat(extension): add a readme [EE-3085] (#6888)
* feat(extension): add a readme [EE-3085]

* add prerequisites
2022-05-11 11:58:11 +12:00
Dmitry Salakhov
45b300eaff fix(settings): allow empty edge url (#6907) 2022-05-10 15:51:12 -03:00
andres-portainer
ad7545f009 fix(tls): downgrade minimum version to TLS 1.2 to avoid proxy problems EE-3152 (#6909) 2022-05-10 15:33:53 -03:00
matias-portainer
5df30b9eb0 chore(edge): add unit tests to edgestatus inspect endpoint EE-3088 (#6905)
* chore(edge): add unit tests to edgestatus inspect endpoint EE-3088
2022-05-10 11:58:19 -03:00
Ali
2e0555dbca refactor(docker networks): migrate docker network detail view to react EE-2196 (#6700)
* Migrate network details to react
2022-05-10 09:01:15 +12:00
itsconquest
9650aa56c7 fix(extension): always restart the backend [EE-3093] (#6890) 2022-05-06 15:14:24 +12:00
itsconquest
0beb0d95c1 fix(extension): add missing labels [EE-3068] (#6879)
* fix(extension): add missing labels [EE-3068]

* fix(extension): add missing labels [EE-3068]
2022-05-06 14:19:26 +12:00
Dakota Walsh
3de585fe17 fix(extension): extend JWT auth token expiration for extension EE-3065 (#6881)
The default expiration time of 8 hours does not make sense in the
context of the docker desktop extension. This adds a new feature flag
which can be enabled with `export DOCKER_EXTENSION=1` and when 
present will set the expiration time to 99 years.

I've set this flag in the docker-compose.yml we use when building our
docker extension.
2022-05-06 09:52:47 +12:00
Chaim Lev-Ari
c732ca2d2f fix(edge): allow more options for url [EE-2975] (#6781) 2022-05-05 10:03:24 +03:00
Chaim Lev-Ari
d4c2ad4a57 fix(edge/aeec): add explanation about PORTAINER_EDGE_ID [EE-3056] (#6874) 2022-05-05 10:02:34 +03:00
wheresolivia
bf59ef50a3 add data-cy to application creation info elements (#6871) 2022-05-02 14:06:54 +12:00
Matt Hook
840a3ce732 switch natural sort lib for a better one (#6862)
Switched to better natural sorting package
2022-05-02 12:37:26 +12:00
Oscar Zhou
f7780cecb3 feat(ci/security): add code dependency security scan and docker image vulnerability scan [EE-2537] (#6853)
This PR supports to scan code security of js and golang dependencies and image vulnerability of locally built docker image
2022-05-02 12:09:45 +12:00
sunportainer
24c61034c1 fix endpoints tag display issue (#6851) 2022-04-30 16:30:40 +08:00
Richard Wei
95b3fff917 fix(filter): EE-2972 - fix filter selector css EE-2972 (#6858)
* fix selector css style

* fix selector css
2022-04-29 15:06:43 +12:00
itsconquest
0f52188261 fix(home): fix styles of edit button [EE-3006] (#6803)
* fix(home): fix styles of edit button [EE-3006]

* fix(home): EE-3006 fix styles of edit button

Co-authored-by: Simon Meng <simon.meng@portainer.io>
2022-04-29 11:01:05 +12:00
itsconquest
b1b0a76465 fix(edge): fix formatting of scripts for release [EE-2987] (#6794)
* fix(edge) fix formatting for release [EE-2987]

* fix(edge) EE-2987 fix edge agent command formatting

Co-authored-by: Simon Meng <simon.meng@portainer.io>
2022-04-29 09:44:34 +12:00
andres-portainer
8a6024ce9b fix(edge-stacks): add an endpoint to delete the status of an edge stack EE-2432 (#6551) 2022-04-28 16:50:23 -03:00
Richard Wei
61a3bfe994 fix clear all button text vertical align (#6833) 2022-04-28 10:18:44 +12:00
Chaim Lev-Ari
842044e759 chore(app): add typescript check and fix errors [EE-3014] (#6822) 2022-04-27 14:10:20 +03:00
Prabhat Khera
b3e035d353 pass tagsPartialMatch query param on home screen (#6842) 2022-04-27 17:27:35 +12:00
Prabhat Khera
33f433ce45 fix status filter (#6827) 2022-04-27 11:40:23 +12:00
itsconquest
abb79ccbeb fix(settings): fix logic for showing https section [EE-3008] (#6805) 2022-04-27 10:48:40 +12:00
cong meng
c340b62f43 fix: EE-3019 add space on top copy button (#6819) 2022-04-27 10:10:49 +12:00
Chaim Lev-Ari
bbb096412d fix(edge): show edge environment in edge views [EE-2997] (#6795) 2022-04-26 14:25:20 +03:00
Chaim Lev-Ari
141a530e28 chore(deps): add tailwindcss [DTD-29] (#6604) 2022-04-26 08:16:46 +03:00
Chaim Lev-Ari
d08b498cb9 refactor(edge): use react poll freq field [EE-2614] (#6757) 2022-04-26 08:14:48 +03:00
Prabhat Khera
bebee78152 fix(home): fix home page filters EE-2972 (#6789) 2022-04-26 12:17:36 +12:00
andres-portainer
5b77edb76d fix(aeec): enforce non-empty EdgeIDs for global key environment retrieval EE-3013 (#6808) 2022-04-25 11:35:14 -03:00
Richard Wei
bcec6a8915 fix add rewrite annotation should not available for traefik (#6799) 2022-04-22 20:02:53 +12:00
Chaim Lev-Ari
3496d5f00b test push 2022-04-22 10:17:03 +03:00
itsconquest
4ee5ae90e7 fix(user-settings): prevent autofocus on access tokens for release [EE-2978] (#6790) 2022-04-22 11:44:54 +12:00
Chaim Lev-Ari
4180e41fa1 fix(edge): generate token when loading settings [EE-2988] (#6793) 2022-04-21 19:18:49 +03:00
Chaim Lev-Ari
5289e4d66b fix(edge): generate token when loading settings [EE-2988] (#6792) 2022-04-21 19:18:42 +03:00
Matt Hook
ace162ec1c bump versions (#6782) 2022-04-20 17:19:08 +12:00
Richard Wei
a9887d4a31 Homepage Filter Component (#6762) 2022-04-20 17:04:05 +12:00
Richard Wei
8ce3e7581b fix: wildcard with hostname validation error issue EE-2101 (#6741)
* fix wildcard validation error
2022-04-20 17:02:21 +12:00
Richard Wei
9de0704775 feat(ingress): support-regex-with-k8s-ingress EE-2644 (#6748)
* support regex with k8s ingress

* remove text for rewrite to /

* added tooltip
2022-04-20 16:45:20 +12:00
cong meng
e20c34e12a feat(password) EE-2690 update the text of force password change hint (#6780)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2022-04-20 11:01:07 +12:00
Chaim Lev-Ari
e217ac7121 feat(edge): show correct heartbeat and sync aeec changes [EE-2876] (#6769) 2022-04-19 21:43:36 +03:00
sunportainer
76d1b70644 fix(volume): prevent bind mounts and allow named volumes [EE-2364] (#6771)
* check bindmounts via absolute path

* check bindmounts via absolute path
2022-04-19 20:05:16 +08:00
Stéphane Busso
360701e256 feat(docker-desktop-extension): Make Portainer compatible with Docker Desktop Extension EE-2747 (#6644)
* Initial extension build

* Add auto login

fix auto auth

add some message

Add extension version

Double attempt to login

Add auto login from jwt check

Add autologin on logout

revert sidebar

Catch error 401 to relogin

cleanup login

Add password generator

Hide User block and collapse sidebar by default

hide user box and toggle sidebar

remove defailt dd

Integrate extension to portainer

Move extension to build

remove files from ignore

Move extension folder

fix alpine

try to copy folder

try add

Change base image

move folder extension

ignore folder build

Fix

relative path

Move ext to root

fix image name

versioned index

Update extension on same image

Update mod

* fix kubeshell baseurl

* Fix kube shell

* move build and remove https

* Tidy mod

* Remove space

* Fix hash test

* Password manager

* change to building locally

* Restore version variable and add local install command

* fix local dev image + hide users & auth

* Password manageListen on locahost onlyr

* FIxes base path

* Hide only username

* Move default to constants

* Update app/portainer/components/PageHeader/HeaderContent.html

Co-authored-by: Chaim Lev-Ari <chiptus@users.noreply.github.com>

* fix 2 failing FE tests [EE-2938]

* remove password autogeneration from v1

* fix webhooks

* fix docker container console and attach

* fix default for portainer IP

* update meta, dockerfile and makefile for new ver

* fix basepath in kube and docker console

* revert makefile changes

* add icon back

* Add remote short cut command

* make local methods the default

* default to 0.0.0 for version for local development

* simplify make commands

* small build fixes

* resolve conflicts

* Update api/filesystem/write.go

Co-authored-by: Chaim Lev-Ari <chiptus@users.noreply.github.com>

* use a more secure default pass

Co-authored-by: itsconquest <william.conquest@portainer.io>
Co-authored-by: Chaim Lev-Ari <chiptus@users.noreply.github.com>
2022-04-19 13:10:42 +12:00
Chaim Lev-Ari
7efdae5eee feat(endpoints): enable env vars on kube edge deploy [EE-2542] (#6620) 2022-04-17 10:34:20 +03:00
sunportainer
da9ef7dfcf fix(download):update the downloads files directory EE-2473 (#6734)
* update the downloads files directory
2022-04-17 11:04:04 +08:00
Chao Geng
69c34cdf0c EE-2767 Correct sidebar hovering info (#6750) 2022-04-17 09:33:58 +08:00
LP B
030b3d7c4d fix(edge-jobs): HTTP 404 on file upload (#6671)
* fix(edge-jobs): HTTP 404 on file upload

* fix(edge-jobs): state 'edge job' in message on edge job removal instead of 'stack'

* fix(api/edge-jobs): save changes on edge-jobs update
2022-04-14 18:44:04 +02:00
Chaim Lev-Ari
355674cf22 fix(datastore): fix test for EdgePortainerUrl [EE-2967] (#6765) 2022-04-14 17:16:10 +03:00
Chaim Lev-Ari
85a7b7e0fc refactor(edge): move edge deploy script to react [EE-2689] (#6747) 2022-04-14 13:14:23 +03:00
Prabhat Khera
328ce2f995 fix migration test (#6763) 2022-04-14 18:57:17 +12:00
Prabhat Khera
e4241207cb fix(database): db migration improvements EE-2688 (#6662) 2022-04-14 16:25:13 +12:00
cong meng
85ad4e334a feat(password) EE-2690 enforce strong password policy (#6751)
* feat(password) EE-2690 enforce strong password policy

* feat(password) EE-2690 disable create user button if password is not valid

* feat(password) EE-2690 show force password change warning only when week password is detected

* feat(password) EE-2690 prevent users leave account page by clicking add access token button

Co-authored-by: Simon Meng <simon.meng@portainer.io>
2022-04-14 13:45:54 +12:00
Dmitry Salakhov
9ebc963082 fix: bump golang deps to resolve CVEs (#6755) 2022-04-14 10:17:00 +12:00
andres-portainer
3178787bc1 feat(edge): implement automatic edge environment creation EE-2848 (#6754) 2022-04-13 10:08:26 -03:00
fhanportainer
b08e0b0235 feat(git): added regex to validate compose file extension (#6731)
* feat(git): added regex to validate compose file extension

* feat(git): copy change

* feat(git): updated icon
2022-04-13 21:57:39 +12:00
LP B
aac2aca912 fix(api/endpoint): refresh kubernetes client cache on endpoint update (#6752) 2022-04-12 17:05:15 +02:00
Chao Geng
f707c90cd3 fix(agent): take agent_secret into account EE-2128 (#6379)
* EE-2128 take agent_sceret into account

* EE-2128 align output code

* EE-2128 fix copy command error

* EE-2128 align code

* EE-2128 fix typo

* Update endpoint.html

remove glint auto changes

* EE-2128 Format html with Prettier

* EE-2128 Adjust UI for dark mode and adopt AGENT_SECRET on k8s automatically

* EE-2128 fix bug created by merge

* EE-2128 Move the initailization of AGENT_SECRET to main.go

* EE-2128 read AGENT_SECRET when settings is initializing
2022-04-12 18:45:58 +08:00
andres-portainer
3eea3e88bc fix(chisel): downgrade back to the previous version of Chisel EE-2718 (#6745) 2022-04-11 14:32:59 -03:00
Chaim Lev-Ari
13faa75a2d fix(endpoints): show edge script when unassociated [EE-2842] (#6730) 2022-04-11 11:26:13 +03:00
sunportainer
287107e8da update go.mod/go.sum (#6732) 2022-04-11 10:26:25 +08:00
Chaim Lev-Ari
2535887984 chore(format): format codebase [EE-2935] (#6746) 2022-04-10 14:05:31 +03:00
Marcelo Rydel
f12c3968f1 feat(edge): edgeStacks and edgeJobs operations small refactors [EE-2744] (#6648) 2022-04-08 11:27:38 -03:00
Dmitry Salakhov
6419e7740a fixed direct dependencies (#6565) 2022-04-08 10:27:58 +12:00
LP B
298e3d263e feat(registry): enforce name uniqueness for registries (#6709)
* feat(app/registries): add name uniqueness validation on registry creation

* feat(api/registry): enforce name uniqueness on registry creation

* feat(api/registry): enforce name uniqueness on registry update

* feat(app/registry): enforce name uniqueness on registry update
2022-04-07 22:58:26 +02:00
andres-portainer
9ffaf47741 fix(endpoint-status): add a redirect to keep backwards compatibility EE-2932 (#6735) 2022-04-07 11:33:14 -03:00
Marcelo Rydel
dff74f0823 feat(ssl): enable mTLS certificates [EE-2617] (#6612) 2022-04-07 11:32:00 -03:00
Marcelo Rydel
f9f937f844 feat(registries): Registry browser for non-admins [EE-2459] (#6549)
* feat(registries): allow non-admin users to see environment registries

* remove unused function

* fix error message

* fix test

* fix imports order

* feat(registry): check access first, add parameters name

* use registryID

* fix(sidebar): allow standard users to see endpoint registries view

Co-authored-by: LP B <xAt0mZ@users.noreply.github.com>
2022-04-07 15:22:31 +02:00
Marcelo Rydel
77e48bfb74 chore(endpoint/edge): small refactor and move endpoint status handler [EE-2710] (#6637) 2022-04-07 09:17:36 -03:00
Dmitry Salakhov
f4ac6f8320 update helm to 3.8.0 (#6564) 2022-04-07 13:30:00 +12:00
Oscar Zhou
bf8b44834a fix(timeout): change the url of the documentation link on timeout page (#6729) 2022-04-07 13:16:28 +12:00
Dakota Walsh
3c98bf9a79 fix(theme): apply theme without saving (#6695) 2022-04-06 09:08:35 +12:00
Prabhat Khera
e1df46b92b add missed migration for DB version 36 (#6678) 2022-04-06 09:07:51 +12:00
Richard Wei
7e28b3ca3f fix issue on editing app with persisted folder (#6646)
Co-authored-by: Richard Wei <dgui.wei@gmail.com>
2022-04-06 05:42:01 +12:00
Oscar Zhou
2059a9e064 feat(adminmonitor): redirect to timeout page if admin is not created in 5 mins [EE-2691] (#6688)
This PR solves the issue that the Portainer instance will be always accessible in certain cases, like `restart: always` setting with docker run, even if the administrator is not created in the first 5 minutes. 
The solution is that the user will be redirected to a timeout page when any actions, such as refresh the page and click button, are made after administrator initialisation window(5 minutes) timeout.
2022-04-05 16:29:57 +12:00
LP B
167825ff3f feat(registries): update registry Password wording to Access Token for Dockerhub (#6557) 2022-04-04 22:54:13 +02:00
LP B
f154e6e0f1 style(app): white text in high contrast for service update dropdown text (#6660) 2022-04-04 15:57:56 +02:00
sunportainer
311129e746 fix(docker):show error for offline endpoint (#6702) 2022-04-04 18:24:47 +08:00
Chao Geng
f59459f936 EE-2463 When add agent to k8s with AGENT_SECRET, AGENT_SECRET should be configured on both side. otherwise, it will get unknown error. this error should be "agent already paired with another Portainer instance" (#6679) 2022-04-01 14:40:44 +08:00
sunportainer
ee90fffce1 fix(template) add notification when delete (#6675) 2022-04-01 14:40:08 +08:00
wheresolivia
4ddd6663f5 add data-cy to mark namespace system button (#6714) 2022-04-01 13:30:56 +13:00
Richard Wei
ec3d7026d4 fix(service): fix service name starting from zero EE-2807 (#6694)
* fix service name index
2022-03-31 10:34:29 +13:00
Richard Wei
fb7f24df9c fix domain name selector issue (#6696)
Co-authored-by: Richard Wei <dgui.wei@gmail.com>
2022-03-30 15:50:11 +13:00
Marcelo Rydel
8860d72f70 fix(edge/jobs): fix get edge job file content [EE-2702] (#6622) 2022-03-28 12:02:09 -03:00
sunportainer
b846c8e6d2 fix(stack): git stack redeploy issue EE-2737 (#6667)
* fix(stack) git stack redeploy issue
2022-03-28 21:31:03 +08:00
sunportainer
379f9e2822 fix(swagger):add swagger for X-Registry-Auth EE-2408 (#6664)
* add swagger for X-Registry-Auth

* Update api-description.md
2022-03-28 21:30:19 +08:00
cong meng
3579b11a8b fix(k8s) deploying manifest using default namespace EE-2104 (#6385)
* fix(k8s) deploying manifest using default namespace EE-2104
2022-03-24 21:28:53 +13:00
wheresolivia
4377aec72b feat(cy):add data-cy to add kube application publishing mode dropdown… (#6687)
* feat(cy):add data-cy to add kube application publishing mode dropdown list, rename the create service button data-cy

* fix prettier issues
2022-03-22 14:54:17 +13:00
4797 changed files with 206474 additions and 98798 deletions

View File

@@ -1,44 +0,0 @@
version: "2"
checks:
argument-count:
enabled: false
complex-logic:
enabled: false
file-lines:
enabled: false
method-complexity:
enabled: false
method-count:
enabled: false
method-lines:
enabled: false
nested-control-flow:
enabled: false
return-statements:
enabled: false
similar-code:
enabled: false
identical-code:
enabled: false
plugins:
gofmt:
enabled: true
eslint:
enabled: true
channel: "eslint-5"
config:
config: .eslintrc.yml
exclude_patterns:
- assets/
- build/
- dist/
- distribution/
- node_modules
- test/
- webpack/
- gruntfile.js
- webpack.config.js
- api/
- "!app/kubernetes/**"
- .github/
- .tmp/

View File

@@ -1,3 +1,5 @@
*
!dist
!build
!metadata.json
!docker-extension/build

View File

@@ -10,6 +10,7 @@ globals:
extends:
- 'eslint:recommended'
- 'plugin:storybook/recommended'
- 'plugin:import/typescript'
- prettier
plugins:
@@ -23,26 +24,43 @@ parserOptions:
modules: true
rules:
no-console: error
no-alert: error
no-control-regex: 'off'
no-empty: warn
no-empty-function: warn
no-useless-escape: 'off'
import/named: error
import/order:
[
'error',
{
pathGroups: [{ pattern: '@/**', group: 'internal' }, { pattern: '{Kubernetes,Portainer,Agent,Azure,Docker}/**', group: 'internal' }],
pathGroups:
[
{ pattern: '@@/**', group: 'internal', position: 'after' },
{ pattern: '@/**', group: 'internal' },
{ pattern: '{Kubernetes,Portainer,Agent,Azure,Docker}/**', group: 'internal' },
],
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
pathGroupsExcludedImportTypes: ['internal'],
},
]
no-restricted-imports:
- error
- patterns:
- group:
- '@/react/test-utils/*'
message: 'These utils are just for test files'
settings:
'import/resolver':
alias:
map:
- ['@@', './app/react/components']
- ['@', './app']
extensions: ['.js', '.ts', '.tsx']
typescript: true
node: true
overrides:
- files:
@@ -52,6 +70,7 @@ overrides:
parser: '@typescript-eslint/parser'
plugins:
- '@typescript-eslint'
- 'regex'
extends:
- airbnb
- airbnb-typescript
@@ -66,13 +85,23 @@ overrides:
settings:
react:
version: 'detect'
rules:
no-console: error
import/order:
['error', { pathGroups: [{ pattern: '@/**', group: 'internal' }], groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'], 'newlines-between': 'always' }]
[
'error',
{
pathGroups: [{ pattern: '@@/**', group: 'internal', position: 'after' }, { pattern: '@/**', group: 'internal' }],
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
'newlines-between': 'always',
},
]
no-plusplus: off
func-style: [error, 'declaration']
import/prefer-default-export: off
no-use-before-define: ['error', { functions: false }]
'@typescript-eslint/no-use-before-define': ['error', { functions: false }]
no-use-before-define: 'off'
'@typescript-eslint/no-use-before-define': ['error', { functions: false, 'allowNamedExports': true }]
no-shadow: 'off'
'@typescript-eslint/no-shadow': off
jsx-a11y/no-autofocus: warn
@@ -85,21 +114,38 @@ overrides:
'@typescript-eslint/explicit-module-boundary-types': off
'@typescript-eslint/no-unused-vars': 'error'
'@typescript-eslint/no-explicit-any': 'error'
'jsx-a11y/label-has-associated-control': ['error', { 'assert': 'either' }]
'jsx-a11y/label-has-associated-control': ['error', { 'assert': 'either', controlComponents: ['Input', 'Checkbox'] }]
'react/function-component-definition': ['error', { 'namedComponents': 'function-declaration' }]
'react/jsx-no-bind': off
'no-await-in-loop': 'off'
'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }]
'regex/invalid': ['error', [{ 'regex': '<Icon icon="(.*)"', 'message': 'Please directly import the `lucide-react` icon instead of using the string' }]]
'@typescript-eslint/no-restricted-imports':
- error
- patterns:
- group:
- '@/react/test-utils/*'
message: 'These utils are just for test files'
overrides: # allow props spreading for hoc files
- files:
- app/**/with*.ts{,x}
rules:
'react/jsx-props-no-spreading': off
- files:
- app/**/*.test.*
extends:
- 'plugin:jest/recommended'
- 'plugin:jest/style'
- 'plugin:vitest/recommended'
env:
'jest/globals': true
'vitest/env': true
rules:
'react/jsx-no-constructed-context-values': off
'@typescript-eslint/no-restricted-imports': off
no-restricted-imports: off
'react/jsx-props-no-spreading': off
- files:
- app/**/*.stories.*
rules:
'no-alert': off
'@typescript-eslint/no-restricted-imports': off
no-restricted-imports: off
'react/jsx-props-no-spreading': off

View File

@@ -2,4 +2,7 @@
cf5056d9c03b62d91a25c3b9127caac838695f98
# prettier v2
42e7db0ae7897d3cb72b0ea1ecf57ee2dd694169
42e7db0ae7897d3cb72b0ea1ecf57ee2dd694169
# tailwind prettier
58d66d3142950bb90a7d85511c034ac9fabba9ba

11
.github/DISCUSSION_TEMPLATE/help.yaml vendored Normal file
View File

@@ -0,0 +1,11 @@
body:
- type: markdown
attributes:
value: |
Before asking a question, make sure it hasn't been already asked and answered. You can search our [discussions](https://github.com/orgs/portainer/discussions) and [bug reports](https://github.com/portainer/portainer/issues) in GitHub. Also, be sure to check our [knowledge base](https://portal.portainer.io/knowledge) and [documentation](https://docs.portainer.io/) first.
- type: textarea
attributes:
label: Ask a Question!
validations:
required: true

38
.github/DISCUSSION_TEMPLATE/ideas.yaml vendored Normal file
View File

@@ -0,0 +1,38 @@
body:
- type: markdown
attributes:
value: |
# Welcome!
Thanks for suggesting an idea for Portainer!
Before opening a new idea or feature request, make sure that we do not have any duplicates already open. You can ensure this by [searching this discussion cagetory](https://github.com/orgs/portainer/discussions/categories/ideas). If there is a duplicate, please add a comment to the existing idea instead.
Also, be sure to check our [knowledge base](https://portal.portainer.io/knowledge) and [documentation](https://docs.portainer.io) as they may point you toward a solution.
**DO NOT FILE DUPLICATE REQUESTS.**
- type: textarea
attributes:
label: Is your feature request related to a problem? Please describe
description: Short list of what the feature request aims to address.
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.
validations:
required: false

View File

@@ -1,54 +0,0 @@
---
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 https://portainer.io/slack/
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/r/portainer-logs)
**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 9443:9443 portainer/portainer`):
- Browser:
- Use Case (delete as appropriate): Using Portainer at Home, Using Portainer in a Commercial setup.
- Have you reviewed our technical documentation and knowledge base? Yes/No
**Additional context**
Add any other context about the problem here.

177
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,177 @@
name: Bug Report
description: Create a report to help us improve.
labels: kind/bug,bug/need-confirmation
body:
- type: markdown
attributes:
value: |
# Welcome!
The issue tracker is for reporting bugs. If you have an [idea for a new feature](https://github.com/orgs/portainer/discussions/categories/ideas) or a [general question about Portainer](https://github.com/orgs/portainer/discussions/categories/help) please post in our [GitHub Discussions](https://github.com/orgs/portainer/discussions).
You can also ask for help in our [community Slack channel](https://join.slack.com/t/portainer/shared_invite/zt-txh3ljab-52QHTyjCqbe5RibC2lcjKA).
**DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS**.
- type: checkboxes
id: terms
attributes:
label: Before you start please confirm the following.
options:
- label: Yes, I've searched similar issues on [GitHub](https://github.com/portainer/portainer/issues).
required: true
- label: Yes, I've checked whether this issue is covered in the Portainer [documentation](https://docs.portainer.io) or [knowledge base](https://portal.portainer.io/knowledge).
required: true
- type: markdown
attributes:
value: |
# About your issue
Tell us a bit about the issue you're having.
How to write a good bug report:
- Respect the issue template as much as possible.
- Summarize the issue so that we understand what is going wrong.
- Describe what you would have expected to have happened, and what actually happened instead.
- Provide easy to follow steps to reproduce the issue.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown).
- type: textarea
attributes:
label: Problem Description
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
attributes:
label: Expected Behavior
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Actual Behavior
description: A clear and concise description of what actually happens.
validations:
required: true
- type: textarea
attributes:
label: Steps to Reproduce
description: Please be as detailed as possible when providing steps to reproduce.
placeholder: |
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
validations:
required: true
- type: textarea
attributes:
label: Portainer logs or screenshots
description: Provide Portainer container logs or any screenshots related to the issue.
validations:
required: false
- type: markdown
attributes:
value: |
# About your environment
Tell us a bit about your Portainer environment.
- type: dropdown
attributes:
label: Portainer version
description: We only provide support for the most recent version of Portainer and the previous 3 versions. If you are on an older version of Portainer we recommend [upgrading first](https://docs.portainer.io/start/upgrade) in case your bug has already been fixed.
multiple: false
options:
- '2.22.0'
- '2.21.3'
- '2.21.2'
- '2.21.1'
- '2.21.0'
- '2.20.3'
- '2.20.2'
- '2.20.1'
- '2.20.0'
- '2.19.5'
- '2.19.4'
- '2.19.3'
- '2.19.2'
- '2.19.1'
- '2.19.0'
- '2.18.4'
- '2.18.3'
- '2.18.2'
- '2.18.1'
- '2.17.1'
- '2.17.0'
- '2.16.2'
- '2.16.1'
- '2.16.0'
validations:
required: true
- type: dropdown
attributes:
label: Portainer Edition
multiple: false
options:
- 'Business Edition (BE/EE) with 5NF / 3NF license'
- 'Business Edition (BE/EE) with Home & Student license'
- 'Business Edition (BE/EE) with Starter license'
- 'Business Edition (BE/EE) with Professional or Enterprise license'
- 'Community Edition (CE)'
validations:
required: true
- type: input
attributes:
label: Platform and Version
description: |
Enter your container management platform (Docker | Swarm | Kubernetes) along with the version.
Example: Docker 24.0.3 | Docker Swarm 24.0.3 | Kubernetes 1.26
You can find our supported platforms [in our documentation](https://docs.portainer.io/start/requirements-and-prerequisites).
validations:
required: true
- type: input
attributes:
label: OS and Architecture
description: |
Enter your Operating System, Version and Architecture. Example: Ubuntu 22.04, AMD64 | Raspbian OS, ARM64
validations:
required: true
- type: input
attributes:
label: Browser
description: |
Enter your browser and version. Example: Google Chrome 114.0
validations:
required: false
- type: textarea
attributes:
label: What command did you use to deploy Portainer?
description: |
Example: `docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest`
If you deployed Portainer using a compose file or manifest you can provide this here as well.
render: bash
validations:
required: false
- type: textarea
attributes:
label: Additional Information
description: Any additional information about your environment, the bug, or anything else you think might be helpful.
validations:
required: false

View File

@@ -1,5 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: Portainer Business Edition - Get 5 nodes free
url: https://portainer.io/pricing/take5
about: Portainer Business Edition has more features, more support and you can now get 5 nodes free for as long as you want.
- name: Question
url: https://github.com/orgs/portainer/discussions/new?category=help
about: Ask us a question about Portainer usage or deployment.
- name: Idea or Feature Request
url: https://github.com/orgs/portainer/discussions/new?category=ideas
about: Suggest an idea or feature/enhancement that should be added in Portainer.
- name: Portainer Business Edition - Get 3 Nodes Free
url: https://www.portainer.io/take-3
about: Portainer Business Edition has more features, more support and you can now get 3 nodes free for as long as you want.

166
.github/workflows/ci.yaml vendored Normal file
View File

@@ -0,0 +1,166 @@
name: ci
on:
workflow_dispatch:
push:
branches:
- 'develop'
- 'release/*'
pull_request:
branches:
- 'develop'
- 'release/*'
- 'feat/*'
- 'fix/*'
- 'refactor/*'
types:
- opened
- reopened
- synchronize
- ready_for_review
env:
DOCKER_HUB_REPO: portainerci/portainer-ce
EXTENSION_HUB_REPO: portainerci/portainer-docker-extension
NODE_VERSION: 18.x
jobs:
build_images:
strategy:
matrix:
config:
- { platform: linux, arch: amd64, version: "" }
- { platform: linux, arch: arm64, version: "" }
- { platform: linux, arch: arm, version: "" }
- { platform: linux, arch: ppc64le, version: "" }
- { platform: windows, arch: amd64, version: 1809 }
- { platform: windows, arch: amd64, version: ltsc2022 }
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- name: '[preparation] checkout the current branch'
uses: actions/checkout@v4.1.1
with:
ref: ${{ github.event.inputs.branch }}
- name: '[preparation] set up golang'
uses: actions/setup-go@v5.0.0
with:
go-version-file: go.mod
- name: '[preparation] set up node.js'
uses: actions/setup-node@v4.0.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
- name: '[preparation] set up qemu'
uses: docker/setup-qemu-action@v3.0.0
- name: '[preparation] set up docker context for buildx'
run: docker context create builders
- name: '[preparation] set up docker buildx'
uses: docker/setup-buildx-action@v3.0.0
with:
endpoint: builders
- name: '[preparation] docker login'
uses: docker/login-action@v3.0.0
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: '[preparation] set the container image tag'
run: |
if [[ "${GITHUB_REF_NAME}" =~ ^release/.*$ ]]; then
# use the release branch name as the tag for release branches
# for instance, release/2.19 becomes 2.19
CONTAINER_IMAGE_TAG=$(echo $GITHUB_REF_NAME | cut -d "/" -f 2)
elif [ "${GITHUB_EVENT_NAME}" == "pull_request" ]; then
# use pr${{ github.event.number }} as the tag for pull requests
# for instance, pr123
CONTAINER_IMAGE_TAG="pr${{ github.event.number }}"
else
# replace / with - in the branch name
# for instance, feature/1.0.0 -> feature-1.0.0
CONTAINER_IMAGE_TAG=$(echo $GITHUB_REF_NAME | sed 's/\//-/g')
fi
echo "CONTAINER_IMAGE_TAG=${CONTAINER_IMAGE_TAG}-${{ matrix.config.platform }}${{ matrix.config.version }}-${{ matrix.config.arch }}" >> $GITHUB_ENV
- name: '[execution] build linux & windows portainer binaries'
run: |
export YARN_VERSION=$(yarn --version)
export WEBPACK_VERSION=$(yarn list webpack --depth=0 | grep webpack | awk -F@ '{print $2}')
export BUILDNUMBER=${GITHUB_RUN_NUMBER}
GIT_COMMIT_HASH_LONG=${{ github.sha }}
export GIT_COMMIT_HASH_SHORT={GIT_COMMIT_HASH_LONG:0:7}
NODE_ENV="testing"
if [[ "${GITHUB_REF_NAME}" =~ ^release/.*$ ]]; then
NODE_ENV="production"
fi
make build-all PLATFORM=${{ matrix.config.platform }} ARCH=${{ matrix.config.arch }} ENV=${NODE_ENV}
env:
CONTAINER_IMAGE_TAG: ${{ env.CONTAINER_IMAGE_TAG }}
- name: '[execution] build and push docker images'
run: |
if [ "${{ matrix.config.platform }}" == "windows" ]; then
mv dist/portainer dist/portainer.exe
docker buildx build --output=type=registry --attest type=provenance,mode=max --attest type=sbom,disabled=false --platform ${{ matrix.config.platform }}/${{ matrix.config.arch }} --build-arg OSVERSION=${{ matrix.config.version }} -t "${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}" -f build/${{ matrix.config.platform }}/Dockerfile .
else
docker buildx build --output=type=registry --attest type=provenance,mode=max --attest type=sbom,disabled=false --platform ${{ matrix.config.platform }}/${{ matrix.config.arch }} -t "${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}" -f build/${{ matrix.config.platform }}/Dockerfile .
docker buildx build --output=type=registry --attest type=provenance,mode=max --attest type=sbom,disabled=false --platform ${{ matrix.config.platform }}/${{ matrix.config.arch }} -t "${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}-alpine" -f build/${{ matrix.config.platform }}/alpine.Dockerfile .
if [[ "${GITHUB_REF_NAME}" =~ ^release/.*$ ]]; then
docker buildx build --output=type=registry --attest type=provenance,mode=max --attest type=sbom,disabled=false --platform ${{ matrix.config.platform }}/${{ matrix.config.arch }} -t "${EXTENSION_HUB_REPO}:${CONTAINER_IMAGE_TAG}" -f build/${{ matrix.config.platform }}/Dockerfile .
docker buildx build --output=type=registry --attest type=provenance,mode=max --attest type=sbom,disabled=false --platform ${{ matrix.config.platform }}/${{ matrix.config.arch }} -t "${EXTENSION_HUB_REPO}:${CONTAINER_IMAGE_TAG}-alpine" -f build/${{ matrix.config.platform }}/alpine.Dockerfile .
fi
fi
env:
CONTAINER_IMAGE_TAG: ${{ env.CONTAINER_IMAGE_TAG }}
build_manifests:
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
needs: [build_images]
steps:
- name: '[preparation] docker login'
uses: docker/login-action@v3.0.0
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: '[preparation] set up docker context for buildx'
run: docker version && docker context create builders
- name: '[preparation] set up docker buildx'
uses: docker/setup-buildx-action@v3.0.0
with:
endpoint: builders
- name: '[execution] build and push manifests'
run: |
if [[ "${GITHUB_REF_NAME}" =~ ^release/.*$ ]]; then
# use the release branch name as the tag for release branches
# for instance, release/2.19 becomes 2.19
CONTAINER_IMAGE_TAG=$(echo $GITHUB_REF_NAME | cut -d "/" -f 2)
elif [ "${GITHUB_EVENT_NAME}" == "pull_request" ]; then
# use pr${{ github.event.number }} as the tag for pull requests
# for instance, pr123
CONTAINER_IMAGE_TAG="pr${{ github.event.number }}"
else
# replace / with - in the branch name
# for instance, feature/1.0.0 -> feature-1.0.0
CONTAINER_IMAGE_TAG=$(echo $GITHUB_REF_NAME | sed 's/\//-/g')
fi
docker buildx imagetools create -t "${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}" \
"${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}-linux-amd64" \
"${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}-linux-arm64" \
"${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}-linux-arm" \
"${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}-linux-ppc64le" \
"${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}-windows1809-amd64" \
"${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}-windowsltsc2022-amd64"
docker buildx imagetools create -t "${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}-alpine" \
"${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}-linux-amd64-alpine" \
"${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}-linux-arm64-alpine" \
"${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}-linux-arm-alpine" \
"${DOCKER_HUB_REPO}:${CONTAINER_IMAGE_TAG}-linux-ppc64le-alpine"
if [[ "${GITHUB_REF_NAME}" =~ ^release/.*$ ]]; then
docker buildx imagetools create -t "${EXTENSION_HUB_REPO}:${CONTAINER_IMAGE_TAG}" \
"${EXTENSION_HUB_REPO}:${CONTAINER_IMAGE_TAG}-linux-amd64" \
"${EXTENSION_HUB_REPO}:${CONTAINER_IMAGE_TAG}-linux-arm64"
fi

View File

@@ -11,5 +11,5 @@ jobs:
with:
CONFLICT_LABEL_NAME: 'has conflicts'
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
MAX_RETRIES: 5
WAIT_MS: 5000
MAX_RETRIES: 10
WAIT_MS: 60000

View File

@@ -11,20 +11,32 @@ on:
- master
- develop
- release/*
types:
- opened
- reopened
- synchronize
- ready_for_review
env:
GO_VERSION: 1.22.5
NODE_VERSION: 18.x
jobs:
run-linters:
name: Run linters
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14'
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
- uses: actions/setup-go@v4
with:
go-version: ${{ env.GO_VERSION }}
- run: yarn --frozen-lockfile
- name: Run linters
uses: wearerequired/lint-action@v1
with:
@@ -34,3 +46,10 @@ jobs:
prettier_dir: app/
gofmt: true
gofmt_dir: api/
- name: Typecheck
uses: icrawl/action-tsc@v1
- name: GolangCI-Lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.59.1
args: --timeout=10m -c .golangci.yaml

View File

@@ -0,0 +1,254 @@
name: Nightly Code Security Scan
on:
schedule:
- cron: '0 20 * * *'
workflow_dispatch:
env:
GO_VERSION: 1.22.5
DOCKER_HUB_REPO: portainerci/portainer-ce
DOCKER_HUB_IMAGE_TAG: develop
jobs:
client-dependencies:
name: Client Dependency Check
runs-on: ubuntu-latest
if: >- # only run for develop branch
github.ref == 'refs/heads/develop'
outputs:
js: ${{ steps.set-matrix.outputs.js_result }}
steps:
- name: checkout repository
uses: actions/checkout@master
- name: scan vulnerabilities by Snyk
uses: snyk/actions/node@master
continue-on-error: true # To make sure that artifact upload gets called
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
json: true
- name: upload scan result as develop artifact
uses: actions/upload-artifact@v3
with:
name: js-security-scan-develop-result
path: snyk.json
- name: develop scan report export to html
run: |
$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=snyk --path="/data/snyk.json" --output-type=table --export --export-filename="/data/js-result")
- name: upload html file as artifact
uses: actions/upload-artifact@v3
with:
name: html-js-result-${{github.run_id}}
path: js-result.html
- name: analyse vulnerabilities
id: set-matrix
run: |
result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=snyk --path="/data/snyk.json" --output-type=matrix)
echo "js_result=${result}" >> $GITHUB_OUTPUT
server-dependencies:
name: Server Dependency Check
runs-on: ubuntu-latest
if: >- # only run for develop branch
github.ref == 'refs/heads/develop'
outputs:
go: ${{ steps.set-matrix.outputs.go_result }}
steps:
- name: checkout repository
uses: actions/checkout@master
- name: install Go
uses: actions/setup-go@v3
with:
go-version: ${{ env.GO_VERSION }}
- name: download Go modules
run: cd ./api && go get -t -v -d ./...
- name: scan vulnerabilities by Snyk
continue-on-error: true # To make sure that artifact upload gets called
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run: |
yarn global add snyk
snyk test --file=./go.mod --json-file-output=snyk.json 2>/dev/null || :
- name: upload scan result as develop artifact
uses: actions/upload-artifact@v3
with:
name: go-security-scan-develop-result
path: snyk.json
- name: develop scan report export to html
run: |
$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=snyk --path="/data/snyk.json" --output-type=table --export --export-filename="/data/go-result")
- name: upload html file as artifact
uses: actions/upload-artifact@v3
with:
name: html-go-result-${{github.run_id}}
path: go-result.html
- name: analyse vulnerabilities
id: set-matrix
run: |
result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=snyk --path="/data/snyk.json" --output-type=matrix)
echo "go_result=${result}" >> $GITHUB_OUTPUT
image-vulnerability:
name: Image Vulnerability Check
runs-on: ubuntu-latest
if: >-
github.ref == 'refs/heads/develop'
outputs:
image-trivy: ${{ steps.set-trivy-matrix.outputs.image_trivy_result }}
image-docker-scout: ${{ steps.set-docker-scout-matrix.outputs.image_docker_scout_result }}
steps:
- name: scan vulnerabilities by Trivy
uses: docker://docker.io/aquasec/trivy:latest
continue-on-error: true
with:
args: image --ignore-unfixed=true --vuln-type="os,library" --exit-code=1 --format="json" --output="image-trivy.json" --no-progress ${{ env.DOCKER_HUB_REPO }}:${{ env.DOCKER_HUB_IMAGE_TAG }}
- name: upload Trivy image security scan result as artifact
uses: actions/upload-artifact@v3
with:
name: image-security-scan-develop-result
path: image-trivy.json
- name: develop Trivy scan report export to html
run: |
$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=trivy --path="/data/image-trivy.json" --output-type=table --export --export-filename="/data/image-trivy-result")
- name: upload html file as Trivy artifact
uses: actions/upload-artifact@v3
with:
name: html-image-result-${{github.run_id}}
path: image-trivy-result.html
- name: analyse vulnerabilities from Trivy
id: set-trivy-matrix
run: |
result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=trivy --path="/data/image-trivy.json" --output-type=matrix)
echo "image_trivy_result=${result}" >> $GITHUB_OUTPUT
- name: scan vulnerabilities by Docker Scout
uses: docker/scout-action@v1
continue-on-error: true
with:
command: cves
image: ${{ env.DOCKER_HUB_REPO }}:${{ env.DOCKER_HUB_IMAGE_TAG }}
sarif-file: image-docker-scout.json
dockerhub-user: ${{ secrets.DOCKER_HUB_USERNAME }}
dockerhub-password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: upload Docker Scout image security scan result as artifact
uses: actions/upload-artifact@v3
with:
name: image-security-scan-develop-result
path: image-docker-scout.json
- name: develop Docker Scout scan report export to html
run: |
$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=docker-scout --path="/data/image-docker-scout.json" --output-type=table --export --export-filename="/data/image-docker-scout-result")
- name: upload html file as Docker Scout artifact
uses: actions/upload-artifact@v3
with:
name: html-image-result-${{github.run_id}}
path: image-docker-scout-result.html
- name: analyse vulnerabilities from Docker Scout
id: set-docker-scout-matrix
run: |
result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest summary --report-type=docker-scout --path="/data/image-docker-scout.json" --output-type=matrix)
echo "image_docker_scout_result=${result}" >> $GITHUB_OUTPUT
result-analysis:
name: Analyse Scan Results
needs: [client-dependencies, server-dependencies, image-vulnerability]
runs-on: ubuntu-latest
if: >-
github.ref == 'refs/heads/develop'
strategy:
matrix:
js: ${{fromJson(needs.client-dependencies.outputs.js)}}
go: ${{fromJson(needs.server-dependencies.outputs.go)}}
image-trivy: ${{fromJson(needs.image-vulnerability.outputs.image-trivy)}}
image-docker-scout: ${{fromJson(needs.image-vulnerability.outputs.image-docker-scout)}}
steps:
- name: display the results of js, Go, and image scan
run: |
echo "${{ matrix.js.status }}"
echo "${{ matrix.go.status }}"
echo "${{ matrix.image-trivy.status }}"
echo "${{ matrix.image-docker-scout.status }}"
echo "${{ matrix.js.summary }}"
echo "${{ matrix.go.summary }}"
echo "${{ matrix.image-trivy.summary }}"
echo "${{ matrix.image-docker-scout.summary }}"
- name: send message to Slack
if: >-
matrix.js.status == 'failure' ||
matrix.go.status == 'failure' ||
matrix.image-trivy.status == 'failure' ||
matrix.image-docker-scout.status == 'failure'
uses: slackapi/slack-github-action@v1.23.0
with:
payload: |
{
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Code Scanning Result (*${{ github.repository }}*)\n*<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|GitHub Actions Workflow URL>*"
}
}
],
"attachments": [
{
"color": "#FF0000",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*JS dependency check*: *${{ matrix.js.status }}*\n${{ matrix.js.summary }}"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Go dependency check*: *${{ matrix.go.status }}*\n${{ matrix.go.summary }}"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Image Trivy vulnerability check*: *${{ matrix.image-trivy.status }}*\n${{ matrix.image-trivy.summary }}\n"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Image Docker Scout vulnerability check*: *${{ matrix.image-docker-scout.status }}*\n${{ matrix.image-docker-scout.summary }}\n"
}
}
]
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SECURITY_SLACK_WEBHOOK_URL }}
SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK

298
.github/workflows/pr-security.yml vendored Normal file
View File

@@ -0,0 +1,298 @@
name: PR Code Security Scan
on:
pull_request_review:
types:
- submitted
- edited
paths:
- 'package.json'
- 'go.mod'
- 'build/linux/Dockerfile'
- 'build/linux/alpine.Dockerfile'
- 'build/windows/Dockerfile'
- '.github/workflows/pr-security.yml'
env:
GO_VERSION: 1.22.5
NODE_VERSION: 18.x
jobs:
client-dependencies:
name: Client Dependency Check
runs-on: ubuntu-latest
if: >-
github.event.pull_request &&
github.event.review.body == '/scan' &&
github.event.pull_request.draft == false
outputs:
jsdiff: ${{ steps.set-diff-matrix.outputs.js_diff_result }}
steps:
- name: checkout repository
uses: actions/checkout@master
- name: scan vulnerabilities by Snyk
uses: snyk/actions/node@master
continue-on-error: true # To make sure that artifact upload gets called
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
json: true
- name: upload scan result as pull-request artifact
uses: actions/upload-artifact@v3
with:
name: js-security-scan-feat-result
path: snyk.json
- name: download artifacts from develop branch built by nightly scan
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mv ./snyk.json ./js-snyk-feature.json
(gh run download -n js-security-scan-develop-result -R ${{ github.repository }} 2>&1 >/dev/null) || :
if [[ -e ./snyk.json ]]; then
mv ./snyk.json ./js-snyk-develop.json
else
echo "null" > ./js-snyk-develop.json
fi
- name: pr vs develop scan report comparison export to html
run: |
$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest diff --report-type=snyk --path="/data/js-snyk-feature.json" --compare-to="/data/js-snyk-develop.json" --output-type=table --export --export-filename="/data/js-result")
- name: upload html file as artifact
uses: actions/upload-artifact@v3
with:
name: html-js-result-compare-to-develop-${{github.run_id}}
path: js-result.html
- name: analyse different vulnerabilities against develop branch
id: set-diff-matrix
run: |
result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest diff --report-type=snyk --path="/data/js-snyk-feature.json" --compare-to="/data/js-snyk-develop.json" --output-type=matrix)
echo "js_diff_result=${result}" >> $GITHUB_OUTPUT
server-dependencies:
name: Server Dependency Check
runs-on: ubuntu-latest
if: >-
github.event.pull_request &&
github.event.review.body == '/scan' &&
github.event.pull_request.draft == false
outputs:
godiff: ${{ steps.set-diff-matrix.outputs.go_diff_result }}
steps:
- name: checkout repository
uses: actions/checkout@master
- name: install Go
uses: actions/setup-go@v3
with:
go-version: ${{ env.GO_VERSION }}
- name: download Go modules
run: cd ./api && go get -t -v -d ./...
- name: scan vulnerabilities by Snyk
continue-on-error: true # To make sure that artifact upload gets called
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run: |
yarn global add snyk
snyk test --file=./go.mod --json-file-output=snyk.json 2>/dev/null || :
- name: upload scan result as pull-request artifact
uses: actions/upload-artifact@v3
with:
name: go-security-scan-feature-result
path: snyk.json
- name: download artifacts from develop branch built by nightly scan
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mv ./snyk.json ./go-snyk-feature.json
(gh run download -n go-security-scan-develop-result -R ${{ github.repository }} 2>&1 >/dev/null) || :
if [[ -e ./snyk.json ]]; then
mv ./snyk.json ./go-snyk-develop.json
else
echo "null" > ./go-snyk-develop.json
fi
- name: pr vs develop scan report comparison export to html
run: |
$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest diff --report-type=snyk --path="/data/go-snyk-feature.json" --compare-to="/data/go-snyk-develop.json" --output-type=table --export --export-filename="/data/go-result")
- name: upload html file as artifact
uses: actions/upload-artifact@v3
with:
name: html-go-result-compare-to-develop-${{github.run_id}}
path: go-result.html
- name: analyse different vulnerabilities against develop branch
id: set-diff-matrix
run: |
result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest diff --report-type=snyk --path="/data/go-snyk-feature.json" --compare-to="/data/go-snyk-develop.json" --output-type=matrix)
echo "go_diff_result=${result}" >> $GITHUB_OUTPUT
image-vulnerability:
name: Image Vulnerability Check
runs-on: ubuntu-latest
if: >-
github.event.pull_request &&
github.event.review.body == '/scan' &&
github.event.pull_request.draft == false
outputs:
imagediff-trivy: ${{ steps.set-diff-trivy-matrix.outputs.image_diff_trivy_result }}
imagediff-docker-scout: ${{ steps.set-diff-docker-scout-matrix.outputs.image_diff_docker_scout_result }}
steps:
- name: checkout code
uses: actions/checkout@master
- name: install Go
uses: actions/setup-go@v3
with:
go-version: ${{ env.GO_VERSION }}
- name: install Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
- name: Install packages
run: yarn --frozen-lockfile
- name: build
run: make build-all
- name: set up docker buildx
uses: docker/setup-buildx-action@v2
- name: build and compress image
uses: docker/build-push-action@v4
with:
context: .
file: build/linux/Dockerfile
tags: local-portainer:${{ github.sha }}
outputs: type=docker,dest=/tmp/local-portainer-image.tar
- name: load docker image
run: |
docker load --input /tmp/local-portainer-image.tar
- name: scan vulnerabilities by Trivy
uses: docker://docker.io/aquasec/trivy:latest
continue-on-error: true
with:
args: image --ignore-unfixed=true --vuln-type="os,library" --exit-code=1 --format="json" --output="image-trivy.json" --no-progress local-portainer:${{ github.sha }}
- name: upload Trivy image security scan result as artifact
uses: actions/upload-artifact@v3
with:
name: image-security-scan-feature-result
path: image-trivy.json
- name: download Trivy artifacts from develop branch built by nightly scan
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mv ./image-trivy.json ./image-trivy-feature.json
(gh run download -n image-security-scan-develop-result -R ${{ github.repository }} 2>&1 >/dev/null) || :
if [[ -e ./image-trivy.json ]]; then
mv ./image-trivy.json ./image-trivy-develop.json
else
echo "null" > ./image-trivy-develop.json
fi
- name: pr vs develop Trivy scan report comparison export to html
run: |
$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest diff --report-type=trivy --path="/data/image-trivy-feature.json" --compare-to="/data/image-trivy-develop.json" --output-type=table --export --export-filename="/data/image-trivy-result")
- name: upload html file as Trivy artifact
uses: actions/upload-artifact@v3
with:
name: html-image-result-compare-to-develop-${{github.run_id}}
path: image-trivy-result.html
- name: analyse different vulnerabilities against develop branch by Trivy
id: set-diff-trivy-matrix
run: |
result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest diff --report-type=trivy --path="/data/image-trivy-feature.json" --compare-to="/data/image-trivy-develop.json" --output-type=matrix)
echo "image_diff_trivy_result=${result}" >> $GITHUB_OUTPUT
- name: scan vulnerabilities by Docker Scout
uses: docker/scout-action@v1
continue-on-error: true
with:
command: cves
image: local-portainer:${{ github.sha }}
sarif-file: image-docker-scout.json
dockerhub-user: ${{ secrets.DOCKER_HUB_USERNAME }}
dockerhub-password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: upload Docker Scout image security scan result as artifact
uses: actions/upload-artifact@v3
with:
name: image-security-scan-feature-result
path: image-docker-scout.json
- name: download Docker Scout artifacts from develop branch built by nightly scan
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
mv ./image-docker-scout.json ./image-docker-scout-feature.json
(gh run download -n image-security-scan-develop-result -R ${{ github.repository }} 2>&1 >/dev/null) || :
if [[ -e ./image-docker-scout.json ]]; then
mv ./image-docker-scout.json ./image-docker-scout-develop.json
else
echo "null" > ./image-docker-scout-develop.json
fi
- name: pr vs develop Docker Scout scan report comparison export to html
run: |
$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest diff --report-type=docker-scout --path="/data/image-docker-scout-feature.json" --compare-to="/data/image-docker-scout-develop.json" --output-type=table --export --export-filename="/data/image-docker-scout-result")
- name: upload html file as Docker Scout artifact
uses: actions/upload-artifact@v3
with:
name: html-image-result-compare-to-develop-${{github.run_id}}
path: image-docker-scout-result.html
- name: analyse different vulnerabilities against develop branch by Docker Scout
id: set-diff-docker-scout-matrix
run: |
result=$(docker run --rm -v ${{ github.workspace }}:/data portainerci/code-security-report:latest diff --report-type=docker-scout --path="/data/image-docker-scout-feature.json" --compare-to="/data/image-docker-scout-develop.json" --output-type=matrix)
echo "image_diff_docker_scout_result=${result}" >> $GITHUB_OUTPUT
result-analysis:
name: Analyse Scan Result Against develop Branch
needs: [client-dependencies, server-dependencies, image-vulnerability]
runs-on: ubuntu-latest
if: >-
github.event.pull_request &&
github.event.review.body == '/scan' &&
github.event.pull_request.draft == false
strategy:
matrix:
jsdiff: ${{fromJson(needs.client-dependencies.outputs.jsdiff)}}
godiff: ${{fromJson(needs.server-dependencies.outputs.godiff)}}
imagediff-trivy: ${{fromJson(needs.image-vulnerability.outputs.imagediff-trivy)}}
imagediff-docker-scout: ${{fromJson(needs.image-vulnerability.outputs.imagediff-docker-scout)}}
steps:
- name: check job status of diff result
if: >-
matrix.jsdiff.status == 'failure' ||
matrix.godiff.status == 'failure' ||
matrix.imagediff-trivy.status == 'failure' ||
matrix.imagediff-docker-scout.status == 'failure'
run: |
echo "${{ matrix.jsdiff.status }}"
echo "${{ matrix.godiff.status }}"
echo "${{ matrix.imagediff-trivy.status }}"
echo "${{ matrix.imagediff-docker-scout.status }}"
echo "${{ matrix.jsdiff.summary }}"
echo "${{ matrix.godiff.summary }}"
echo "${{ matrix.imagediff-trivy.summary }}"
echo "${{ matrix.imagediff-docker-scout.summary }}"
exit 1

View File

@@ -1,7 +1,8 @@
name: Close Stale Issues
on:
schedule:
- cron: '0 12 * * *'
- cron: '0 12 * * *'
workflow_dispatch:
jobs:
stale:
runs-on: ubuntu-latest
@@ -9,7 +10,7 @@ jobs:
issues: write
steps:
- uses: actions/stale@v4.0.0
- uses: actions/stale@v8
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,15 +0,0 @@
name: Test Frontend
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: '14'
cache: 'yarn'
- run: yarn install --frozen-lockfile
- name: Run tests
run: yarn test:client

76
.github/workflows/test.yaml vendored Normal file
View File

@@ -0,0 +1,76 @@
name: Test
env:
GO_VERSION: 1.22.5
NODE_VERSION: 18.x
on:
workflow_dispatch:
pull_request:
branches:
- master
- develop
- release/*
types:
- opened
- reopened
- synchronize
- ready_for_review
push:
branches:
- master
- develop
- release/*
jobs:
test-client:
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- name: 'checkout the current branch'
uses: actions/checkout@v4.1.1
with:
ref: ${{ github.event.inputs.branch }}
- name: 'set up node.js'
uses: actions/setup-node@v4.0.1
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
- run: yarn --frozen-lockfile
- name: Run tests
run: make test-client ARGS="--maxWorkers=2 --minWorkers=1"
test-server:
strategy:
matrix:
config:
- { platform: linux, arch: amd64 }
- { platform: linux, arch: arm64 }
- { platform: windows, arch: amd64, version: 1809 }
- { platform: windows, arch: amd64, version: ltsc2022 }
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- name: 'checkout the current branch'
uses: actions/checkout@v4.1.1
with:
ref: ${{ github.event.inputs.branch }}
- name: 'set up golang'
uses: actions/setup-go@v5.0.0
with:
go-version: ${{ env.GO_VERSION }}
- name: 'install dependencies'
run: make test-deps PLATFORM=linux ARCH=amd64
- name: 'update $PATH'
run: echo "$(pwd)/dist" >> $GITHUB_PATH
- name: 'run tests'
run: make test-server

View File

@@ -0,0 +1,39 @@
name: Validate OpenAPI specs
on:
pull_request:
branches:
- master
- develop
- 'release/*'
types:
- opened
- reopened
- synchronize
- ready_for_review
env:
GO_VERSION: 1.22.5
NODE_VERSION: 18.x
jobs:
openapi-spec:
runs-on: ubuntu-latest
if: github.event.pull_request.draft == false
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: ${{ env.GO_VERSION }}
- name: Download golang modules
run: cd ./api && go get -t -v -d ./...
- uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
- run: yarn --frozen-lockfile
- name: Validate OpenAPI Spec
run: make docs-validate

View File

@@ -1,53 +0,0 @@
name: Validate
on:
pull_request:
branches:
- master
- develop
- 'release/*'
jobs:
openapi-spec:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Setup Node v14
uses: actions/setup-node@v2
with:
node-version: 14
# https://github.com/actions/cache/blob/main/examples.md#node---yarn
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Setup Go v1.17.3
uses: actions/setup-go@v2
with:
go-version: '^1.17.3'
- name: Prebuild docs
run: yarn prebuild:docs
- name: Build OpenAPI 2.0 Spec
run: yarn build:docs
# Install dependencies globally to bypass installing all frontend deps
- name: Install swagger2openapi and swagger-cli
run: yarn global add swagger2openapi @apidevtools/swagger-cli
# OpenAPI2.0 does not support multiple body params (which we utilise in some of our handlers).
# OAS3.0 however does support multiple body params - hence its best to convert the generated OAS 2.0
# to OAS 3.0 and validate the output of generated OAS 3.0 instead.
- name: Convert OpenAPI 2.0 to OpenAPI 3.0 and validate spec
run: yarn validate:docs

5
.gitignore vendored
View File

@@ -7,11 +7,14 @@ storybook-static
.tmp
**/.vscode/settings.json
**/.vscode/tasks.json
.vscode
*.DS_Store
.eslintcache
__debug_bin
__debug_bin*
api/docs
.idea
.env
go.work.sum

39
.golangci.yaml Normal file
View File

@@ -0,0 +1,39 @@
linters:
# Disable all linters, the defaults don't pass on our code yet
disable-all: true
# Enable these for now
enable:
- unused
- depguard
- gosimple
- govet
- errorlint
linters-settings:
depguard:
rules:
main:
deny:
- pkg: 'encoding/json'
desc: 'use github.com/segmentio/encoding/json'
- pkg: 'github.com/sirupsen/logrus'
desc: 'logging is allowed only by github.com/rs/zerolog'
- pkg: 'golang.org/x/exp'
desc: 'exp is not allowed'
- pkg: 'github.com/portainer/libcrypto'
desc: 'use github.com/portainer/portainer/pkg/libcrypto'
- pkg: 'github.com/portainer/libhttp'
desc: 'use github.com/portainer/portainer/pkg/libhttp'
files:
- '!**/*_test.go'
- '!**/base.go'
- '!**/base_tx.go'
# errorlint is causing a typecheck error for some reason. The go compiler will report these
# anyway, so ignore them from the linter
issues:
exclude-rules:
- path: ./
linters:
- typecheck

4
.husky/pre-commit Executable file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn lint-staged

View File

@@ -1 +1,2 @@
dist
dist
api/datastore/test_data

View File

@@ -2,18 +2,24 @@
"printWidth": 180,
"singleQuote": true,
"htmlWhitespaceSensitivity": "strict",
"trailingComma": "es5",
"overrides": [
{
"files": ["*.html"],
"files": [
"*.html"
],
"options": {
"parser": "angular"
}
},
{
"files": ["*.{j,t}sx", "*.ts"],
"files": [
"*.{j,t}sx",
"*.ts"
],
"options": {
"printWidth": 80
}
}
]
}
}

View File

@@ -1,35 +0,0 @@
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
module.exports = {
stories: ['../app/**/*.stories.mdx', '../app/**/*.stories.@(ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
{
name: '@storybook/addon-postcss',
options: {
cssLoaderOptions: {
importLoaders: 1,
modules: {
localIdentName: '[path][name]__[local]',
auto: true,
exportLocalsConvention: 'camelCaseOnly',
},
},
},
},
],
webpackFinal: (config) => {
config.resolve.plugins = [
...(config.resolve.plugins || []),
new TsconfigPathsPlugin({
extensions: config.resolve.extensions,
}),
];
return config;
},
core: {
builder: 'webpack5',
},
staticDirs: ['./public'],
};

93
.storybook/main.ts Normal file
View File

@@ -0,0 +1,93 @@
import { StorybookConfig } from '@storybook/react-webpack5';
import TsconfigPathsPlugin from 'tsconfig-paths-webpack-plugin';
import { Configuration } from 'webpack';
import postcss from 'postcss';
const config: StorybookConfig = {
stories: ['../app/**/*.stories.@(ts|tsx)'],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
{
name: '@storybook/addon-styling',
options: {
cssLoaderOptions: {
importLoaders: 1,
modules: {
localIdentName: '[path][name]__[local]',
auto: true,
exportLocalsConvention: 'camelCaseOnly',
},
},
postCss: {
implementation: postcss,
},
},
},
],
webpackFinal: (config) => {
const rules = config?.module?.rules || [];
const imageRule = rules.find((rule) => {
const test = (rule as { test: RegExp }).test;
if (!test) {
return false;
}
return test.test('.svg');
}) as { [key: string]: any };
imageRule.exclude = /\.svg$/;
rules.unshift({
test: /\.svg$/i,
type: 'asset',
resourceQuery: {
not: [/c/],
}, // exclude react component if *.svg?url
});
rules.unshift({
test: /\.svg$/i,
issuer: /\.(js|ts)(x)?$/,
resourceQuery: /c/,
// *.svg?c
use: [
{
loader: '@svgr/webpack',
options: {
icon: true,
},
},
],
});
return {
...config,
resolve: {
...config.resolve,
plugins: [
...(config.resolve?.plugins || []),
new TsconfigPathsPlugin({
extensions: config.resolve?.extensions,
}),
],
},
module: {
...config.module,
rules,
},
} satisfies Configuration;
},
staticDirs: ['./public'],
typescript: {
reactDocgen: 'react-docgen-typescript',
},
framework: {
name: '@storybook/react-webpack5',
options: {},
},
};
export default config;

View File

@@ -1,41 +0,0 @@
import '../app/assets/css';
import { pushStateLocationPlugin, UIRouter } from '@uirouter/react';
import { initialize as initMSW, mswDecorator } from 'msw-storybook-addon';
import { handlers } from '@/setup-tests/server-handlers';
// Initialize MSW
initMSW({
onUnhandledRequest: ({ method, url }) => {
if (url.pathname.startsWith('/api')) {
console.error(`Unhandled ${method} request to ${url}.
This exception has been only logged in the console, however, it's strongly recommended to resolve this error as you don't want unmocked data in Storybook stories.
If you wish to mock an error response, please refer to this guide: https://mswjs.io/docs/recipes/mocking-error-responses
`);
}
},
});
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
msw: {
handlers,
},
};
export const decorators = [
(Story) => (
<UIRouter plugins={[pushStateLocationPlugin]}>
<Story />
</UIRouter>
),
mswDecorator,
];

51
.storybook/preview.tsx Normal file
View File

@@ -0,0 +1,51 @@
import '../app/assets/css';
import React from 'react';
import { pushStateLocationPlugin, UIRouter } from '@uirouter/react';
import { initialize as initMSW, mswLoader } from 'msw-storybook-addon';
import { handlers } from '../app/setup-tests/server-handlers';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
initMSW(
{
onUnhandledRequest: ({ method, url }) => {
if (url.startsWith('/api')) {
console.error(`Unhandled ${method} request to ${url}.
This exception has been only logged in the console, however, it's strongly recommended to resolve this error as you don't want unmocked data in Storybook stories.
If you wish to mock an error response, please refer to this guide: https://mswjs.io/docs/recipes/mocking-error-responses
`);
}
},
},
handlers
);
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
msw: {
handlers,
},
};
const testQueryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
});
export const decorators = [
(Story) => (
<QueryClientProvider client={testQueryClient}>
<UIRouter plugins={[pushStateLocationPlugin]}>
<Story />
</UIRouter>
</QueryClientProvider>
),
];
export const loaders = [mswLoader];

View File

@@ -2,22 +2,22 @@
/* tslint:disable */
/**
* Mock Service Worker (0.36.3).
* Mock Service Worker (2.0.11).
* @see https://github.com/mswjs/msw
* - Please do NOT modify this file.
* - Please do NOT serve this file on production.
*/
const INTEGRITY_CHECKSUM = '02f4ad4a2797f85668baf196e553d929';
const bypassHeaderName = 'x-msw-bypass';
const INTEGRITY_CHECKSUM = 'c5f7f8e188b673ea4e677df7ea3c5a39';
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse');
const activeClientIds = new Set();
self.addEventListener('install', function () {
return self.skipWaiting();
self.skipWaiting();
});
self.addEventListener('activate', async function (event) {
return self.clients.claim();
self.addEventListener('activate', function (event) {
event.waitUntil(self.clients.claim());
});
self.addEventListener('message', async function (event) {
@@ -33,7 +33,9 @@ self.addEventListener('message', async function (event) {
return;
}
const allClients = await self.clients.matchAll();
const allClients = await self.clients.matchAll({
type: 'window',
});
switch (event.data) {
case 'KEEPALIVE_REQUEST': {
@@ -83,165 +85,8 @@ self.addEventListener('message', async function (event) {
}
});
// Resolve the "main" client for the given event.
// Client that issues a request doesn't necessarily equal the client
// that registered the worker. It's with the latter the worker should
// communicate with during the response resolving phase.
async function resolveMainClient(event) {
const client = await self.clients.get(event.clientId);
if (client.frameType === 'top-level') {
return client;
}
const allClients = await self.clients.matchAll();
return allClients
.filter((client) => {
// Get only those clients that are currently visible.
return client.visibilityState === 'visible';
})
.find((client) => {
// Find the client ID that's recorded in the
// set of clients that have registered the worker.
return activeClientIds.has(client.id);
});
}
async function handleRequest(event, requestId) {
const client = await resolveMainClient(event);
const response = await getResponse(event, client, requestId);
// Send back the response clone for the "response:*" life-cycle events.
// Ensure MSW is active and ready to handle the message, otherwise
// this message will pend indefinitely.
if (client && activeClientIds.has(client.id)) {
(async function () {
const clonedResponse = response.clone();
sendToClient(client, {
type: 'RESPONSE',
payload: {
requestId,
type: clonedResponse.type,
ok: clonedResponse.ok,
status: clonedResponse.status,
statusText: clonedResponse.statusText,
body: clonedResponse.body === null ? null : await clonedResponse.text(),
headers: serializeHeaders(clonedResponse.headers),
redirected: clonedResponse.redirected,
},
});
})();
}
return response;
}
async function getResponse(event, client, requestId) {
const { request } = event;
const requestClone = request.clone();
const getOriginalResponse = () => fetch(requestClone);
// Bypass mocking when the request client is not active.
if (!client) {
return getOriginalResponse();
}
// Bypass initial page load requests (i.e. static assets).
// The absence of the immediate/parent client in the map of the active clients
// means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
// and is not ready to handle requests.
if (!activeClientIds.has(client.id)) {
return await getOriginalResponse();
}
// Bypass requests with the explicit bypass header
if (requestClone.headers.get(bypassHeaderName) === 'true') {
const cleanRequestHeaders = serializeHeaders(requestClone.headers);
// Remove the bypass header to comply with the CORS preflight check.
delete cleanRequestHeaders[bypassHeaderName];
const originalRequest = new Request(requestClone, {
headers: new Headers(cleanRequestHeaders),
});
return fetch(originalRequest);
}
// Send the request to the client-side MSW.
const reqHeaders = serializeHeaders(request.headers);
const body = await request.text();
const clientMessage = await sendToClient(client, {
type: 'REQUEST',
payload: {
id: requestId,
url: request.url,
method: request.method,
headers: reqHeaders,
cache: request.cache,
mode: request.mode,
credentials: request.credentials,
destination: request.destination,
integrity: request.integrity,
redirect: request.redirect,
referrer: request.referrer,
referrerPolicy: request.referrerPolicy,
body,
bodyUsed: request.bodyUsed,
keepalive: request.keepalive,
},
});
switch (clientMessage.type) {
case 'MOCK_SUCCESS': {
return delayPromise(() => respondWithMock(clientMessage), clientMessage.payload.delay);
}
case 'MOCK_NOT_FOUND': {
return getOriginalResponse();
}
case 'NETWORK_ERROR': {
const { name, message } = clientMessage.payload;
const networkError = new Error(message);
networkError.name = name;
// Rejecting a request Promise emulates a network error.
throw networkError;
}
case 'INTERNAL_ERROR': {
const parsedBody = JSON.parse(clientMessage.payload.body);
console.error(
`\
[MSW] Uncaught exception in the request handler for "%s %s":
${parsedBody.location}
This exception has been gracefully handled as a 500 response, however, it's strongly recommended to resolve this error, as it indicates a mistake in your code. If you wish to mock an error response, please see this guide: https://mswjs.io/docs/recipes/mocking-error-responses\
`,
request.method,
request.url
);
return respondWithMock(clientMessage);
}
}
return getOriginalResponse();
}
self.addEventListener('fetch', function (event) {
const { request } = event;
const accept = request.headers.get('accept') || '';
// Bypass server-sent events.
if (accept.includes('text/event-stream')) {
return;
}
// Bypass navigation requests.
if (request.mode === 'navigate') {
@@ -261,36 +106,149 @@ self.addEventListener('fetch', function (event) {
return;
}
const requestId = uuidv4();
return event.respondWith(
handleRequest(event, requestId).catch((error) => {
if (error.name === 'NetworkError') {
console.warn('[MSW] Successfully emulated a network error for the "%s %s" request.', request.method, request.url);
return;
}
// At this point, any exception indicates an issue with the original request/response.
console.error(
`\
[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`,
request.method,
request.url,
`${error.name}: ${error.message}`
);
})
);
// Generate unique request ID.
const requestId = crypto.randomUUID();
event.respondWith(handleRequest(event, requestId));
});
function serializeHeaders(headers) {
const reqHeaders = {};
headers.forEach((value, name) => {
reqHeaders[name] = reqHeaders[name] ? [].concat(reqHeaders[name]).concat(value) : value;
});
return reqHeaders;
async function handleRequest(event, requestId) {
const client = await resolveMainClient(event);
const response = await getResponse(event, client, requestId);
// Send back the response clone for the "response:*" life-cycle events.
// Ensure MSW is active and ready to handle the message, otherwise
// this message will pend indefinitely.
if (client && activeClientIds.has(client.id)) {
(async function () {
const responseClone = response.clone();
sendToClient(
client,
{
type: 'RESPONSE',
payload: {
requestId,
isMockedResponse: IS_MOCKED_RESPONSE in response,
type: responseClone.type,
status: responseClone.status,
statusText: responseClone.statusText,
body: responseClone.body,
headers: Object.fromEntries(responseClone.headers.entries()),
},
},
[responseClone.body]
);
})();
}
return response;
}
function sendToClient(client, message) {
// Resolve the main client for the given event.
// Client that issues a request doesn't necessarily equal the client
// that registered the worker. It's with the latter the worker should
// communicate with during the response resolving phase.
async function resolveMainClient(event) {
const client = await self.clients.get(event.clientId);
if (client?.frameType === 'top-level') {
return client;
}
const allClients = await self.clients.matchAll({
type: 'window',
});
return allClients
.filter((client) => {
// Get only those clients that are currently visible.
return client.visibilityState === 'visible';
})
.find((client) => {
// Find the client ID that's recorded in the
// set of clients that have registered the worker.
return activeClientIds.has(client.id);
});
}
async function getResponse(event, client, requestId) {
const { request } = event;
// Clone the request because it might've been already used
// (i.e. its body has been read and sent to the client).
const requestClone = request.clone();
function passthrough() {
const headers = Object.fromEntries(requestClone.headers.entries());
// Remove internal MSW request header so the passthrough request
// complies with any potential CORS preflight checks on the server.
// Some servers forbid unknown request headers.
delete headers['x-msw-intention'];
return fetch(requestClone, { headers });
}
// Bypass mocking when the client is not active.
if (!client) {
return passthrough();
}
// Bypass initial page load requests (i.e. static assets).
// The absence of the immediate/parent client in the map of the active clients
// means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
// and is not ready to handle requests.
if (!activeClientIds.has(client.id)) {
return passthrough();
}
// Bypass requests with the explicit bypass header.
// Such requests can be issued by "ctx.fetch()".
const mswIntention = request.headers.get('x-msw-intention');
if (['bypass', 'passthrough'].includes(mswIntention)) {
return passthrough();
}
// Notify the client that a request has been intercepted.
const requestBuffer = await request.arrayBuffer();
const clientMessage = await sendToClient(
client,
{
type: 'REQUEST',
payload: {
id: requestId,
url: request.url,
mode: request.mode,
method: request.method,
headers: Object.fromEntries(request.headers.entries()),
cache: request.cache,
credentials: request.credentials,
destination: request.destination,
integrity: request.integrity,
redirect: request.redirect,
referrer: request.referrer,
referrerPolicy: request.referrerPolicy,
body: requestBuffer,
keepalive: request.keepalive,
},
},
[requestBuffer]
);
switch (clientMessage.type) {
case 'MOCK_RESPONSE': {
return respondWithMock(clientMessage.data);
}
case 'MOCK_NOT_FOUND': {
return passthrough();
}
}
return passthrough();
}
function sendToClient(client, message, transferrables = []) {
return new Promise((resolve, reject) => {
const channel = new MessageChannel();
@@ -302,27 +260,25 @@ function sendToClient(client, message) {
resolve(event.data);
};
client.postMessage(JSON.stringify(message), [channel.port2]);
client.postMessage(message, [channel.port2].concat(transferrables.filter(Boolean)));
});
}
function delayPromise(cb, duration) {
return new Promise((resolve) => {
setTimeout(() => resolve(cb()), duration);
});
}
async function respondWithMock(response) {
// Setting response status code to 0 is a no-op.
// However, when responding with a "Response.error()", the produced Response
// instance will have status code set to 0. Since it's not possible to create
// a Response instance with status code 0, handle that use-case separately.
if (response.status === 0) {
return Response.error();
}
function respondWithMock(clientMessage) {
return new Response(clientMessage.payload.body, {
...clientMessage.payload,
headers: clientMessage.payload.headers,
});
}
const mockedResponse = new Response(response.body, response);
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
const r = (Math.random() * 16) | 0;
const v = c == 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
value: true,
enumerable: true,
});
return mockedResponse;
}

View File

@@ -15,6 +15,15 @@
// ],
// "description": "Log output to console"
// }
"React Named Export Component": {
"prefix": "rnec",
"body": [
"export function $TM_FILENAME_BASE() {",
" return <div>$TM_FILENAME_BASE</div>;",
"}"
],
"description": "React Named Export Component"
},
"Component": {
"scope": "javascript",
"prefix": "mycomponent",

View File

@@ -25,7 +25,7 @@ Each commit message should include a **type**, a **scope** and a **subject**:
<type>(<scope>): <subject>
```
Lines should not exceed 100 characters. This allows the message to be easier to read on github as well as in various git tools and produces a nice, neat commit log ie:
Lines should not exceed 100 characters. This allows the message to be easier to read on GitHub as well as in various git tools and produces a nice, neat commit log ie:
```
#271 feat(containers): add exposed ports in the containers view
@@ -63,7 +63,7 @@ The subject contains succinct description of the change:
## Contribution process
Our contribution process is described below. Some of the steps can be visualized inside Github via specific `status/` labels, such as `status/1-functional-review` or `status/2-technical-review`.
Our contribution process is described below. Some of the steps can be visualized inside GitHub via specific `status/` labels, such as `status/1-functional-review` or `status/2-technical-review`.
### Bug report
@@ -79,30 +79,42 @@ The feature request process is similar to the bug report process but has an extr
Ensure you have Docker, Node.js, yarn, and Golang installed in the correct versions.
Install dependencies with yarn:
Install dependencies:
```sh
$ yarn
$ make deps
```
Then build and run the project in a Docker container:
```sh
$ yarn start
$ make dev
```
Portainer can now be accessed at <https://localhost:9443>.
Portainer server can now be accessed at <https://localhost:9443>. and UI dev server runs on <http://localhost:8999>.
Find more detailed steps at <https://documentation.portainer.io/contributing/instructions/>.
if you want to build the project you can run:
### Build customisation
```sh
make build-all
```
You can customise the following settings:
For additional make commands, run `make help`.
Find more detailed steps at <https://docs.portainer.io/contribute/build>.
### Build customization
You can customize the following settings:
- `PORTAINER_DATA`: The host dir or volume name used by portainer (default is `/tmp/portainer`, which won't persist over reboots).
- `PORTAINER_PROJECT`: The root dir of the repository - `${portainerRoot}/dist/` is imported into the container to get the build artifacts and external tools (defaults to `your current dir`).
- `PORTAINER_FLAGS`: a list of flags to be used on the portainer commandline, in the form `--admin-password=<pwd hash> --feat fdo=false --feat open-amt` (default: `""`).
## Testing your build
The `--log-level=DEBUG` flag can be passed to the Portainer container in order to provide additional debug output which may be useful when troubleshooting your builds. Please note that this flag was originally intended for internal use and as such the format, functionality and output may change between releases without warning.
## 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:

131
Makefile Normal file
View File

@@ -0,0 +1,131 @@
# See: https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63
# For a list of valid GOOS and GOARCH values
# Note: these can be overriden on the command line e.g. `make PLATFORM=<platform> ARCH=<arch>`
PLATFORM=$(shell go env GOOS)
ARCH=$(shell go env GOARCH)
# build target, can be one of "production", "testing", "development"
ENV=development
WEBPACK_CONFIG=webpack/webpack.$(ENV).js
TAG=local
SWAG=go run github.com/swaggo/swag/cmd/swag@v1.16.2
GOTESTSUM=go run gotest.tools/gotestsum@latest
# Don't change anything below this line unless you know what you're doing
.DEFAULT_GOAL := help
##@ Building
.PHONY: init-dist build-storybook build build-client build-server build-image devops
init-dist:
@mkdir -p dist
build-all: deps build-server build-client ## Build the client, server and download external dependancies (doesn't build an image)
build-client: init-dist ## Build the client
export NODE_ENV=$(ENV) && yarn build --config $(WEBPACK_CONFIG)
build-server: init-dist ## Build the server binary
./build/build_binary.sh "$(PLATFORM)" "$(ARCH)"
build-image: build-all ## Build the Portainer image locally
docker buildx build --load -t portainerci/portainer-ce:$(TAG) -f build/linux/Dockerfile .
build-storybook: ## Build and serve the storybook files
yarn storybook:build
devops: clean deps build-client ## Build the everything target specifically for CI
echo "Building the devops binary..."
@./build/build_binary_azuredevops.sh "$(PLATFORM)" "$(ARCH)"
##@ Build dependencies
.PHONY: deps server-deps client-deps tidy
deps: server-deps client-deps ## Download all client and server build dependancies
server-deps: init-dist ## Download dependant server binaries
@./build/download_binaries.sh $(PLATFORM) $(ARCH)
client-deps: ## Install client dependencies
yarn
tidy: ## Tidy up the go.mod file
cd api && go mod tidy
##@ Cleanup
.PHONY: clean
clean: ## Remove all build and download artifacts
@echo "Clearing the dist directory..."
@rm -rf dist/*
##@ Testing
.PHONY: test test-client test-server
test: test-server test-client ## Run all tests
test-deps: init-dist
./build/download_docker_compose_binary.sh $(PLATFORM) $(ARCH) $(shell jq -r '.dockerCompose' < "./binary-version.json")
test-client: ## Run client tests
yarn test $(ARGS)
test-server: ## Run server tests
$(GOTESTSUM) --format pkgname-and-test-fails --format-hide-empty-pkg --hide-summary skipped -- -cover ./...
##@ Dev
.PHONY: dev dev-client dev-server
dev: ## Run both the client and server in development mode
make dev-server
make dev-client
dev-client: ## Run the client in development mode
yarn dev
dev-server: build-server ## Run the server in development mode
@./dev/run_container.sh
dev-server-podman: build-server ## Run the server in development mode
@./dev/run_container_podman.sh
##@ Format
.PHONY: format format-client format-server
format: format-client format-server ## Format all code
format-client: ## Format client code
yarn format
format-server: ## Format server code
go fmt ./...
##@ Lint
.PHONY: lint lint-client lint-server
lint: lint-client lint-server ## Lint all code
lint-client: ## Lint client code
yarn lint
lint-server: ## Lint server code
golangci-lint run --timeout=10m -c .golangci.yaml
##@ Extension
.PHONY: dev-extension
dev-extension: build-server build-client ## Run the extension in development mode
make local -f build/docker-extension/Makefile
##@ Docs
.PHONY: docs-build docs-validate docs-clean docs-validate-clean
docs-build: init-dist ## Build docs
cd api && $(SWAG) init -o "../dist/docs" -ot "yaml" -g ./http/handler/handler.go --parseDependency --parseInternal --parseDepth 2 -p pascalcase --markdownFiles ./
docs-validate: docs-build ## Validate docs
yarn swagger2openapi --warnOnly dist/docs/swagger.yaml -o dist/docs/openapi.yaml
yarn swagger-cli validate dist/docs/openapi.yaml
##@ Helpers
.PHONY: help
help: ## Display this help
@awk 'BEGIN {FS = ":.*##"; printf "Usage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

View File

@@ -9,26 +9,20 @@ Portainer consists of a single container that can run on any cluster. It can be
**Portainer Business Edition** builds on the open-source base and includes a range of advanced features and functions (like RBAC and Support) that are specific to the needs of business users.
- [Compare Portainer CE and Compare Portainer BE](https://portainer.io/products)
- [Take5 get 5 free nodes of Portainer Business for as long as you want them](https://portainer.io/pricing/take5)
- [Take3 get 3 free nodes of Portainer Business for as long as you want them](https://www.portainer.io/take-3)
- [Portainer BE install guide](https://install.portainer.io)
## Demo
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**.
## Latest Version
Portainer CE is updated regularly. We aim to do an update release every couple of months.
**The latest version of Portainer is 2.9.x**. Portainer is on version 2, the second number denotes the month of release.
[![latest version](https://img.shields.io/github/v/release/portainer/portainer?color=%2344cc11&label=Latest%20release&style=for-the-badge)](https://github.com/portainer/portainer/releases/latest)
## Getting started
- [Deploy Portainer](https://docs.portainer.io/v/ce-2.9/start/install)
- [Documentation](https://documentation.portainer.io)
- [Contribute to the project](https://documentation.portainer.io/contributing/instructions/)
- [Deploy Portainer](https://docs.portainer.io/start/install)
- [Documentation](https://docs.portainer.io)
- [Contribute to the project](https://docs.portainer.io/contribute/contribute)
## Features & Functions
@@ -36,23 +30,22 @@ View [this](https://www.portainer.io/products) table to see all of the Portainer
- [Portainer CE for Docker / Docker Swarm](https://www.portainer.io/solutions/docker)
- [Portainer CE for Kubernetes](https://www.portainer.io/solutions/kubernetes-ui)
- [Portainer CE for Azure ACI](https://www.portainer.io/solutions/serverless-containers)
## Getting help
Portainer CE is an open source project and is supported by the community. You can buy a supported version of Portainer at portainer.io
Learn more about Portainer's community support channels [here.](https://www.portainer.io/community_help)
Learn more about Portainer's community support channels [here.](https://www.portainer.io/get-support-for-portainer)
- Issues: https://github.com/portainer/portainer/issues
- Slack (chat): [https://portainer.io/slack](https://portainer.io/slack)
You can join the Portainer Community by visiting community.portainer.io. This will give you advance notice of events, content and other related Portainer content.
You can join the Portainer Community by visiting [https://www.portainer.io/join-our-community](https://www.portainer.io/join-our-community). This will give you advance notice of events, content and other related Portainer content.
## 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://documentation.portainer.io/contributing/instructions/) to build it locally and make a pull request.
- Want to help us build **_portainer_**? Follow our [contribution guidelines](https://docs.portainer.io/contribute/contribute) to build it locally and make a pull request.
## Security
@@ -66,7 +59,7 @@ If you are a developer, and our code in this repo makes sense to you, we would l
**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.
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/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

View File

@@ -2,30 +2,36 @@ package adminmonitor
import (
"context"
"log"
"net/http"
"strings"
"sync"
"time"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/rs/zerolog/log"
)
var logFatalf = log.Fatalf
const RedirectReasonAdminInitTimeout string = "AdminInitTimeout"
type Monitor struct {
timeout time.Duration
datastore dataservices.DataStore
shutdownCtx context.Context
cancellationFunc context.CancelFunc
mu sync.Mutex
timeout time.Duration
datastore dataservices.DataStore
shutdownCtx context.Context
cancellationFunc context.CancelFunc
mu sync.RWMutex
adminInitDisabled bool
}
// 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 dataservices.DataStore, shutdownCtx context.Context) *Monitor {
return &Monitor{
timeout: timeout,
datastore: datastore,
shutdownCtx: shutdownCtx,
timeout: timeout,
datastore: datastore,
shutdownCtx: shutdownCtx,
adminInitDisabled: false,
}
}
@@ -42,20 +48,29 @@ func (m *Monitor) Start() {
m.cancellationFunc = cancellationFunc
go func() {
log.Println("[DEBUG] [internal,init] [message: start initialization monitor ]")
log.Debug().Msg("start initialization monitor")
select {
case <-time.After(m.timeout):
initialized, err := m.WasInitialized()
if err != nil {
logFatalf("%s", err)
log.Error().Err(err).Msg("AdminMonitor failed to determine if Portainer is Initialized")
return
}
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())
log.Info().Msg("the Portainer instance timed out for security purposes, to re-enable your Portainer instance, you will need to restart Portainer")
m.mu.Lock()
defer m.mu.Unlock()
m.adminInitDisabled = true
return
}
case <-cancellationCtx.Done():
log.Println("[DEBUG] [internal,init] [message: canceling initialization monitor]")
log.Debug().Msg("canceling initialization monitor")
case <-m.shutdownCtx.Done():
log.Println("[DEBUG] [internal,init] [message: shutting down initialization monitor]")
log.Debug().Msg("shutting down initialization monitor")
}
}()
}
@@ -68,6 +83,7 @@ func (m *Monitor) Stop() {
if m.cancellationFunc == nil {
return
}
m.cancellationFunc()
m.cancellationFunc = nil
}
@@ -78,5 +94,27 @@ func (m *Monitor) WasInitialized() (bool, error) {
if err != nil {
return false, err
}
return len(users) > 0, nil
}
func (m *Monitor) WasInstanceDisabled() bool {
m.mu.RLock()
defer m.mu.RUnlock()
return m.adminInitDisabled
}
// WithRedirect checks whether administrator initialisation timeout. If so, it will return the error with redirect reason.
// Otherwise, it will pass through the request to next
func (m *Monitor) WithRedirect(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if m.WasInstanceDisabled() && strings.HasPrefix(r.RequestURI, "/api") && r.RequestURI != "/api/status" && r.RequestURI != "/api/settings/public" {
w.Header().Set("redirect-reason", RedirectReasonAdminInitTimeout)
httperror.WriteError(w, http.StatusSeeOther, "Administrator initialization timeout", nil)
return
}
next.ServeHTTP(w, r)
})
}

View File

@@ -42,28 +42,13 @@ func Test_canStopStartedMonitor(t *testing.T) {
assert.Nil(t, monitor.cancellationFunc, "cancellation function should absent in stopped monitor")
}
func Test_start_shouldFatalAfterTimeout_ifNotInitialized(t *testing.T) {
func Test_start_shouldDisableInstanceAfterTimeout_ifNotInitialized(t *testing.T) {
timeout := 10 * time.Millisecond
datastore := i.NewDatastore(i.WithUsers([]portainer.User{}))
ch := make(chan struct{})
var fataled bool
origLogFatalf := logFatalf
logFatalf = func(s string, v ...interface{}) {
fataled = true
close(ch)
}
defer func() {
logFatalf = origLogFatalf
}()
monitor := New(timeout, datastore, context.Background())
monitor.Start()
<-time.After(2 * timeout)
<-ch
assert.True(t, fataled, "monitor should been timeout and fatal")
<-time.After(20 * timeout)
assert.True(t, monitor.WasInstanceDisabled(), "monitor should have been timeout and instance is disabled")
}

74
api/agent/version.go Normal file
View File

@@ -0,0 +1,74 @@
package agent
import (
"crypto/tls"
"errors"
"fmt"
"io"
"net/http"
"strconv"
"time"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/url"
)
// GetAgentVersionAndPlatform returns the agent version and platform
//
// it sends a ping to the agent and parses the version and platform from the headers
func GetAgentVersionAndPlatform(endpointUrl string, tlsConfig *tls.Config) (portainer.AgentPlatform, string, error) {
httpCli := &http.Client{
Timeout: 3 * time.Second,
}
if tlsConfig != nil {
httpCli.Transport = &http.Transport{
TLSClientConfig: tlsConfig,
}
}
parsedURL, err := url.ParseURL(endpointUrl + "/ping")
if err != nil {
return 0, "", err
}
parsedURL.Scheme = "https"
req, err := http.NewRequest(http.MethodGet, parsedURL.String(), nil)
if err != nil {
return 0, "", err
}
resp, err := httpCli.Do(req)
if err != nil {
return 0, "", err
}
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
if resp.StatusCode != http.StatusNoContent {
return 0, "", fmt.Errorf("Failed request with status %d", resp.StatusCode)
}
version := resp.Header.Get(portainer.PortainerAgentHeader)
if version == "" {
return 0, "", errors.New("Version Header is missing")
}
agentPlatformHeader := resp.Header.Get(portainer.HTTPResponseAgentPlatform)
if agentPlatformHeader == "" {
return 0, "", errors.New("Agent Platform Header is missing")
}
agentPlatformNumber, err := strconv.Atoi(agentPlatformHeader)
if err != nil {
return 0, "", err
}
if agentPlatformNumber == 0 {
return 0, "", errors.New("Agent platform is invalid")
}
return portainer.AgentPlatform(agentPlatformNumber), version, nil
}

View File

@@ -50,4 +50,15 @@ Instead, it acts as a reverse-proxy to the Docker HTTP API. This means that you
To do so, you can use the `/endpoints/{id}/docker` Portainer API environment(endpoint) (which is not documented below due to Swagger limitations). This environment(endpoint) has a restricted access policy so you still need to be authenticated to be able to query this environment(endpoint). Any query on this environment(endpoint) will be proxied to the Docker API of the associated environment(endpoint) (requests and responses objects are the same as documented in the Docker API).
# Private Registry
Using private registry, you will need to pass a based64 encoded JSON string {"registryId":\<registryID value\>} inside the Request Header. The parameter name is "X-Registry-Auth".
\<registryID value\> - The registry ID where the repository was created.
Example:
```
eyJyZWdpc3RyeUlkIjoxfQ==
```
**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://documentation.portainer.io/api/api-examples/).

View File

@@ -1,30 +1,17 @@
package apikey
import (
"crypto/rand"
"io"
portainer "github.com/portainer/portainer/api"
)
// APIKeyService represents a service for managing API keys.
type APIKeyService interface {
HashRaw(rawKey string) []byte
HashRaw(rawKey string) string
GenerateApiKey(user portainer.User, description string) (string, *portainer.APIKey, error)
GetAPIKey(apiKeyID portainer.APIKeyID) (*portainer.APIKey, error)
GetAPIKeys(userID portainer.UserID) ([]portainer.APIKey, error)
GetDigestUserAndKey(digest []byte) (portainer.User, portainer.APIKey, error)
GetDigestUserAndKey(digest string) (portainer.User, portainer.APIKey, error)
UpdateAPIKey(apiKey *portainer.APIKey) error
DeleteAPIKey(apiKeyID portainer.APIKeyID) error
InvalidateUserKeyCache(userId portainer.UserID) bool
}
// generateRandomKey generates a random key of specified length
// source: https://github.com/gorilla/securecookie/blob/master/securecookie.go#L515
func generateRandomKey(length int) []byte {
k := make([]byte, length)
if _, err := io.ReadFull(rand.Reader, k); err != nil {
return nil
}
return k
}

View File

@@ -33,17 +33,19 @@ func Test_generateRandomKey(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := generateRandomKey(tt.wantLenth)
got := GenerateRandomKey(tt.wantLenth)
is.Equal(tt.wantLenth, len(got))
})
}
t.Run("Generated keys are unique", func(t *testing.T) {
keys := make(map[string]bool)
for i := 0; i < 100; i++ {
key := generateRandomKey(8)
for range 100 {
key := GenerateRandomKey(8)
_, ok := keys[string(key)]
is.False(ok)
keys[string(key)] = true
}
})

View File

@@ -1,69 +1,79 @@
package apikey
import (
lru "github.com/hashicorp/golang-lru"
portainer "github.com/portainer/portainer/api"
lru "github.com/hashicorp/golang-lru"
)
const defaultAPIKeyCacheSize = 1024
const DefaultAPIKeyCacheSize = 1024
// entry is a tuple containing the user and API key associated to an API key digest
type entry struct {
user portainer.User
type entry[T any] struct {
user T
apiKey portainer.APIKey
}
// apiKeyCache is a concurrency-safe, in-memory cache which primarily exists for to reduce database roundtrips.
type UserCompareFn[T any] func(T, portainer.UserID) bool
// ApiKeyCache is a concurrency-safe, in-memory cache which primarily exists for to reduce database roundtrips.
// We store the api-key digest (keys) and the associated user and key-data (values) in the cache.
// This is required because HTTP requests will contain only the api-key digest in the x-api-key request header;
// digest value must be mapped to a portainer user (and respective key data) for validation.
// This cache is used to avoid multiple database queries to retrieve these user/key associated to the digest.
type apiKeyCache struct {
type ApiKeyCache[T any] struct {
// cache type [string]entry cache (key: string(digest), value: user/key entry)
// note: []byte keys are not supported by golang-lru Cache
cache *lru.Cache
cache *lru.Cache
userCmpFn UserCompareFn[T]
}
// NewAPIKeyCache creates a new cache for API keys
func NewAPIKeyCache(cacheSize int) *apiKeyCache {
func NewAPIKeyCache[T any](cacheSize int, userCompareFn UserCompareFn[T]) *ApiKeyCache[T] {
cache, _ := lru.New(cacheSize)
return &apiKeyCache{cache: cache}
return &ApiKeyCache[T]{cache: cache, userCmpFn: userCompareFn}
}
// Get returns the user/key associated to an api-key's digest
// This is required because HTTP requests will contain the digest of the API key in header,
// the digest value must be mapped to a portainer user.
func (c *apiKeyCache) Get(digest []byte) (portainer.User, portainer.APIKey, bool) {
val, ok := c.cache.Get(string(digest))
func (c *ApiKeyCache[T]) Get(digest string) (T, portainer.APIKey, bool) {
val, ok := c.cache.Get(digest)
if !ok {
return portainer.User{}, portainer.APIKey{}, false
var t T
return t, portainer.APIKey{}, false
}
tuple := val.(entry)
tuple := val.(entry[T])
return tuple.user, tuple.apiKey, true
}
// Set persists a user/key entry to the cache
func (c *apiKeyCache) Set(digest []byte, user portainer.User, apiKey portainer.APIKey) {
c.cache.Add(string(digest), entry{
func (c *ApiKeyCache[T]) Set(digest string, user T, apiKey portainer.APIKey) {
c.cache.Add(digest, entry[T]{
user: user,
apiKey: apiKey,
})
}
// Delete evicts a digest's user/key entry key from the cache
func (c *apiKeyCache) Delete(digest []byte) {
c.cache.Remove(string(digest))
func (c *ApiKeyCache[T]) Delete(digest string) {
c.cache.Remove(digest)
}
// InvalidateUserKeyCache loops through all the api-keys associated to a user and removes them from the cache
func (c *apiKeyCache) InvalidateUserKeyCache(userId portainer.UserID) bool {
func (c *ApiKeyCache[T]) InvalidateUserKeyCache(userId portainer.UserID) bool {
present := false
for _, k := range c.cache.Keys() {
user, _, _ := c.Get([]byte(k.(string)))
if user.ID == userId {
user, _, _ := c.Get(k.(string))
if c.userCmpFn(user, userId) {
present = c.cache.Remove(k)
}
}
return present
}

View File

@@ -10,32 +10,32 @@ import (
func Test_apiKeyCacheGet(t *testing.T) {
is := assert.New(t)
keyCache := NewAPIKeyCache(10)
keyCache := NewAPIKeyCache(10, compareUser)
// pre-populate cache
keyCache.cache.Add(string("foo"), entry{user: portainer.User{}, apiKey: portainer.APIKey{}})
keyCache.cache.Add(string(""), entry{user: portainer.User{}, apiKey: portainer.APIKey{}})
keyCache.cache.Add(string("foo"), entry[portainer.User]{user: portainer.User{}, apiKey: portainer.APIKey{}})
keyCache.cache.Add(string(""), entry[portainer.User]{user: portainer.User{}, apiKey: portainer.APIKey{}})
tests := []struct {
digest []byte
digest string
found bool
}{
{
digest: []byte("foo"),
digest: "foo",
found: true,
},
{
digest: []byte(""),
digest: "",
found: true,
},
{
digest: []byte("bar"),
digest: "bar",
found: false,
},
}
for _, test := range tests {
t.Run(string(test.digest), func(t *testing.T) {
t.Run(test.digest, func(t *testing.T) {
_, _, found := keyCache.Get(test.digest)
is.Equal(test.found, found)
})
@@ -45,43 +45,43 @@ func Test_apiKeyCacheGet(t *testing.T) {
func Test_apiKeyCacheSet(t *testing.T) {
is := assert.New(t)
keyCache := NewAPIKeyCache(10)
keyCache := NewAPIKeyCache(10, compareUser)
// pre-populate cache
keyCache.Set([]byte("bar"), portainer.User{ID: 2}, portainer.APIKey{})
keyCache.Set([]byte("foo"), portainer.User{ID: 1}, portainer.APIKey{})
keyCache.Set("bar", portainer.User{ID: 2}, portainer.APIKey{})
keyCache.Set("foo", portainer.User{ID: 1}, portainer.APIKey{})
// overwrite existing entry
keyCache.Set([]byte("foo"), portainer.User{ID: 3}, portainer.APIKey{})
keyCache.Set("foo", portainer.User{ID: 3}, portainer.APIKey{})
val, ok := keyCache.cache.Get(string("bar"))
is.True(ok)
tuple := val.(entry)
tuple := val.(entry[portainer.User])
is.Equal(portainer.User{ID: 2}, tuple.user)
val, ok = keyCache.cache.Get(string("foo"))
is.True(ok)
tuple = val.(entry)
tuple = val.(entry[portainer.User])
is.Equal(portainer.User{ID: 3}, tuple.user)
}
func Test_apiKeyCacheDelete(t *testing.T) {
is := assert.New(t)
keyCache := NewAPIKeyCache(10)
keyCache := NewAPIKeyCache(10, compareUser)
t.Run("Delete an existing entry", func(t *testing.T) {
keyCache.cache.Add(string("foo"), entry{user: portainer.User{ID: 1}, apiKey: portainer.APIKey{}})
keyCache.Delete([]byte("foo"))
keyCache.cache.Add(string("foo"), entry[portainer.User]{user: portainer.User{ID: 1}, apiKey: portainer.APIKey{}})
keyCache.Delete("foo")
_, ok := keyCache.cache.Get(string("foo"))
is.False(ok)
})
t.Run("Delete a non-existing entry", func(t *testing.T) {
nonPanicFunc := func() { keyCache.Delete([]byte("non-existent-key")) }
nonPanicFunc := func() { keyCache.Delete("non-existent-key") }
is.NotPanics(nonPanicFunc)
})
}
@@ -128,19 +128,19 @@ func Test_apiKeyCacheLRU(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
keyCache := NewAPIKeyCache(test.cacheLen)
keyCache := NewAPIKeyCache(test.cacheLen, compareUser)
for _, key := range test.key {
keyCache.Set([]byte(key), portainer.User{ID: 1}, portainer.APIKey{})
keyCache.Set(key, portainer.User{ID: 1}, portainer.APIKey{})
}
for _, key := range test.foundKeys {
_, _, found := keyCache.Get([]byte(key))
_, _, found := keyCache.Get(key)
is.True(found, "Key %s not found", key)
}
for _, key := range test.evictedKeys {
_, _, found := keyCache.Get([]byte(key))
_, _, found := keyCache.Get(key)
is.False(found, "key %s should have been evicted", key)
}
})
@@ -150,10 +150,10 @@ func Test_apiKeyCacheLRU(t *testing.T) {
func Test_apiKeyCacheInvalidateUserKeyCache(t *testing.T) {
is := assert.New(t)
keyCache := NewAPIKeyCache(10)
keyCache := NewAPIKeyCache(10, compareUser)
t.Run("Removes users keys from cache", func(t *testing.T) {
keyCache.cache.Add(string("foo"), entry{user: portainer.User{ID: 1}, apiKey: portainer.APIKey{}})
keyCache.cache.Add(string("foo"), entry[portainer.User]{user: portainer.User{ID: 1}, apiKey: portainer.APIKey{}})
ok := keyCache.InvalidateUserKeyCache(1)
is.True(ok)
@@ -163,8 +163,8 @@ func Test_apiKeyCacheInvalidateUserKeyCache(t *testing.T) {
})
t.Run("Does not affect other keys", func(t *testing.T) {
keyCache.cache.Add(string("foo"), entry{user: portainer.User{ID: 1}, apiKey: portainer.APIKey{}})
keyCache.cache.Add(string("bar"), entry{user: portainer.User{ID: 2}, apiKey: portainer.APIKey{}})
keyCache.cache.Add(string("foo"), entry[portainer.User]{user: portainer.User{ID: 1}, apiKey: portainer.APIKey{}})
keyCache.cache.Add(string("bar"), entry[portainer.User]{user: portainer.User{ID: 2}, apiKey: portainer.APIKey{}})
ok := keyCache.InvalidateUserKeyCache(1)
is.True(ok)

View File

@@ -1,15 +1,17 @@
package apikey
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
"time"
"github.com/pkg/errors"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/pkg/errors"
)
const portainerAPIKeyPrefix = "ptr_"
@@ -19,30 +21,45 @@ var ErrInvalidAPIKey = errors.New("Invalid API key")
type apiKeyService struct {
apiKeyRepository dataservices.APIKeyRepository
userRepository dataservices.UserService
cache *apiKeyCache
cache *ApiKeyCache[portainer.User]
}
// GenerateRandomKey generates a random key of specified length
// source: https://github.com/gorilla/securecookie/blob/master/securecookie.go#L515
func GenerateRandomKey(length int) []byte {
k := make([]byte, length)
if _, err := io.ReadFull(rand.Reader, k); err != nil {
return nil
}
return k
}
func compareUser(u portainer.User, id portainer.UserID) bool {
return u.ID == id
}
func NewAPIKeyService(apiKeyRepository dataservices.APIKeyRepository, userRepository dataservices.UserService) *apiKeyService {
return &apiKeyService{
apiKeyRepository: apiKeyRepository,
userRepository: userRepository,
cache: NewAPIKeyCache(defaultAPIKeyCacheSize),
cache: NewAPIKeyCache(DefaultAPIKeyCacheSize, compareUser),
}
}
// HashRaw computes a hash digest of provided raw API key.
func (a *apiKeyService) HashRaw(rawKey string) []byte {
func (a *apiKeyService) HashRaw(rawKey string) string {
hashDigest := sha256.Sum256([]byte(rawKey))
return hashDigest[:]
return base64.StdEncoding.EncodeToString(hashDigest[:])
}
// GenerateApiKey generates a raw API key for a user (for one-time display).
// The generated API key is stored in the cache and database.
func (a *apiKeyService) GenerateApiKey(user portainer.User, description string) (string, *portainer.APIKey, error) {
randKey := generateRandomKey(32)
randKey := GenerateRandomKey(32)
encodedRawAPIKey := base64.StdEncoding.EncodeToString(randKey)
prefixedAPIKey := portainerAPIKeyPrefix + encodedRawAPIKey
hashDigest := a.HashRaw(prefixedAPIKey)
apiKey := &portainer.APIKey{
@@ -53,8 +70,7 @@ func (a *apiKeyService) GenerateApiKey(user portainer.User, description string)
Digest: hashDigest,
}
err := a.apiKeyRepository.CreateAPIKey(apiKey)
if err != nil {
if err := a.apiKeyRepository.Create(apiKey); err != nil {
return "", nil, errors.Wrap(err, "Unable to create API key")
}
@@ -66,7 +82,7 @@ func (a *apiKeyService) GenerateApiKey(user portainer.User, description string)
// GetAPIKey returns an API key by its ID.
func (a *apiKeyService) GetAPIKey(apiKeyID portainer.APIKeyID) (*portainer.APIKey, error) {
return a.apiKeyRepository.GetAPIKey(apiKeyID)
return a.apiKeyRepository.Read(apiKeyID)
}
// GetAPIKeys returns all the API keys associated to a user.
@@ -76,8 +92,7 @@ func (a *apiKeyService) GetAPIKeys(userID portainer.UserID) ([]portainer.APIKey,
// GetDigestUserAndKey returns the user and api-key associated to a specified hash digest.
// A cache lookup is performed first; if the user/api-key is not found in the cache, respective database lookups are performed.
func (a *apiKeyService) GetDigestUserAndKey(digest []byte) (portainer.User, portainer.APIKey, error) {
// get api key from cache if possible
func (a *apiKeyService) GetDigestUserAndKey(digest string) (portainer.User, portainer.APIKey, error) {
cachedUser, cachedKey, ok := a.cache.Get(digest)
if ok {
return cachedUser, cachedKey, nil
@@ -88,7 +103,7 @@ func (a *apiKeyService) GetDigestUserAndKey(digest []byte) (portainer.User, port
return portainer.User{}, portainer.APIKey{}, errors.Wrap(err, "Unable to retrieve API key")
}
user, err := a.userRepository.User(apiKey.UserID)
user, err := a.userRepository.Read(apiKey.UserID)
if err != nil {
return portainer.User{}, portainer.APIKey{}, errors.Wrap(err, "Unable to retrieve digest user")
}
@@ -105,21 +120,22 @@ func (a *apiKeyService) UpdateAPIKey(apiKey *portainer.APIKey) error {
if err != nil {
return errors.Wrap(err, "Unable to retrieve API key")
}
a.cache.Set(apiKey.Digest, user, *apiKey)
return a.apiKeyRepository.UpdateAPIKey(apiKey)
return a.apiKeyRepository.Update(apiKey.ID, apiKey)
}
// DeleteAPIKey deletes an API key and removes the digest/api-key entry from the cache.
func (a *apiKeyService) DeleteAPIKey(apiKeyID portainer.APIKeyID) error {
// get api-key digest to remove from cache
apiKey, err := a.apiKeyRepository.GetAPIKey(apiKeyID)
apiKey, err := a.apiKeyRepository.Read(apiKeyID)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Unable to retrieve API key: %d", apiKeyID))
}
// delete the user/api-key from cache
a.cache.Delete(apiKey.Digest)
return a.apiKeyRepository.DeleteAPIKey(apiKeyID)
return a.apiKeyRepository.Delete(apiKeyID)
}
func (a *apiKeyService) InvalidateUserKeyCache(userId portainer.UserID) bool {

View File

@@ -2,7 +2,8 @@ package apikey
import (
"crypto/sha256"
"log"
"encoding/base64"
"fmt"
"strings"
"testing"
"time"
@@ -10,6 +11,8 @@ import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/datastore"
"github.com/stretchr/testify/assert"
"github.com/rs/zerolog/log"
)
func Test_SatisfiesAPIKeyServiceInterface(t *testing.T) {
@@ -20,8 +23,7 @@ func Test_SatisfiesAPIKeyServiceInterface(t *testing.T) {
func Test_GenerateApiKey(t *testing.T) {
is := assert.New(t)
_, store, teardown := datastore.MustNewTestStore(true)
defer teardown()
_, store := datastore.MustNewTestStore(t, true, true)
service := NewAPIKeyService(store.APIKeyRepository(), store.User())
@@ -67,15 +69,14 @@ func Test_GenerateApiKey(t *testing.T) {
generatedDigest := sha256.Sum256([]byte(rawKey))
is.Equal(apiKey.Digest, generatedDigest[:])
is.Equal(apiKey.Digest, base64.StdEncoding.EncodeToString(generatedDigest[:]))
})
}
func Test_GetAPIKey(t *testing.T) {
is := assert.New(t)
_, store, teardown := datastore.MustNewTestStore(true)
defer teardown()
_, store := datastore.MustNewTestStore(t, true, true)
service := NewAPIKeyService(store.APIKeyRepository(), store.User())
@@ -94,8 +95,7 @@ func Test_GetAPIKey(t *testing.T) {
func Test_GetAPIKeys(t *testing.T) {
is := assert.New(t)
_, store, teardown := datastore.MustNewTestStore(true)
defer teardown()
_, store := datastore.MustNewTestStore(t, true, true)
service := NewAPIKeyService(store.APIKeyRepository(), store.User())
@@ -115,8 +115,7 @@ func Test_GetAPIKeys(t *testing.T) {
func Test_GetDigestUserAndKey(t *testing.T) {
is := assert.New(t)
_, store, teardown := datastore.MustNewTestStore(true)
defer teardown()
_, store := datastore.MustNewTestStore(t, true, true)
service := NewAPIKeyService(store.APIKeyRepository(), store.User())
@@ -151,8 +150,7 @@ func Test_GetDigestUserAndKey(t *testing.T) {
func Test_UpdateAPIKey(t *testing.T) {
is := assert.New(t)
_, store, teardown := datastore.MustNewTestStore(true)
defer teardown()
_, store := datastore.MustNewTestStore(t, true, true)
service := NewAPIKeyService(store.APIKeyRepository(), store.User())
@@ -169,11 +167,9 @@ func Test_UpdateAPIKey(t *testing.T) {
_, apiKeyGot, err := service.GetDigestUserAndKey(apiKey.Digest)
is.NoError(err)
log.Println(apiKey)
log.Println(apiKeyGot)
log.Debug().Str("wanted", fmt.Sprintf("%+v", apiKey)).Str("got", fmt.Sprintf("%+v", apiKeyGot)).Msg("")
is.Equal(apiKey.LastUsed, apiKeyGot.LastUsed)
})
t.Run("Successfully updates api-key in cache upon api-key update", func(t *testing.T) {
@@ -199,8 +195,7 @@ func Test_UpdateAPIKey(t *testing.T) {
func Test_DeleteAPIKey(t *testing.T) {
is := assert.New(t)
_, store, teardown := datastore.MustNewTestStore(true)
defer teardown()
_, store := datastore.MustNewTestStore(t, true, true)
service := NewAPIKeyService(store.APIKeyRepository(), store.User())
@@ -240,8 +235,7 @@ func Test_DeleteAPIKey(t *testing.T) {
func Test_InvalidateUserKeyCache(t *testing.T) {
is := assert.New(t)
_, store, teardown := datastore.MustNewTestStore(true)
defer teardown()
_, store := datastore.MustNewTestStore(t, true, true)
service := NewAPIKeyService(store.APIKeyRepository(), store.User())

View File

@@ -34,3 +34,45 @@ func TarFileInBuffer(fileContent []byte, fileName string, mode int64) ([]byte, e
return buffer.Bytes(), nil
}
// tarFileInBuffer represents a tar archive buffer.
type tarFileInBuffer struct {
b *bytes.Buffer
w *tar.Writer
}
func NewTarFileInBuffer() *tarFileInBuffer {
var b bytes.Buffer
return &tarFileInBuffer{
b: &b,
w: tar.NewWriter(&b),
}
}
// Put puts a single file to tar archive buffer.
func (t *tarFileInBuffer) Put(fileContent []byte, fileName string, mode int64) error {
hdr := &tar.Header{
Name: fileName,
Mode: mode,
Size: int64(len(fileContent)),
}
if err := t.w.WriteHeader(hdr); err != nil {
return err
}
if _, err := t.w.Write(fileContent); err != nil {
return err
}
return nil
}
// Bytes returns the archive as a byte array.
func (t *tarFileInBuffer) Bytes() []byte {
return t.b.Bytes()
}
func (t *tarFileInBuffer) Close() error {
return t.w.Close()
}

View File

@@ -3,6 +3,7 @@ package archive
import (
"archive/tar"
"compress/gzip"
"errors"
"fmt"
"io"
"os"
@@ -47,18 +48,6 @@ func TarGzDir(absolutePath string) (string, error) {
}
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
}
@@ -67,6 +56,26 @@ func addToArchive(tarWriter *tar.Writer, pathInArchive string, path string, info
if err != nil {
return err
}
stat, err := file.Stat()
if err != nil {
return err
}
header, err := tar.FileInfoHeader(stat, stat.Name())
if err != nil {
return err
}
header.Name = pathInArchive // use relative paths in archive
err = tarWriter.WriteHeader(header)
if err != nil {
return err
}
if stat.IsDir() {
return nil
}
_, err = io.Copy(tarWriter, file)
return err
}
@@ -84,7 +93,7 @@ func ExtractTarGz(r io.Reader, outputDirPath string) error {
for {
header, err := tarReader.Next()
if err == io.EOF {
if errors.Is(err, io.EOF) {
break
}
@@ -97,7 +106,7 @@ func ExtractTarGz(r io.Reader, outputDirPath string) error {
// 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 {
if err := os.MkdirAll(filepath.Dir(p), 0o744); err != nil {
return fmt.Errorf("Failed to extract dir %s", filepath.Dir(p))
}
outFile, err := os.Create(p)
@@ -109,7 +118,7 @@ func ExtractTarGz(r io.Reader, outputDirPath string) error {
}
outFile.Close()
default:
return fmt.Errorf("Tar: uknown type: %v in %s",
return fmt.Errorf("tar: unknown type: %v in %s",
header.Typeflag,
header.Name)
}

View File

@@ -2,14 +2,12 @@ package archive
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"testing"
"github.com/docker/docker/pkg/ioutils"
"github.com/stretchr/testify/assert"
)
@@ -27,22 +25,18 @@ func listFiles(dir string) []string {
}
func Test_shouldCreateArhive(t *testing.T) {
tmpdir, _ := ioutils.TempDir("", "backup")
defer os.RemoveAll(tmpdir)
tmpdir := t.TempDir()
content := []byte("content")
ioutil.WriteFile(path.Join(tmpdir, "outer"), content, 0600)
os.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)
os.WriteFile(path.Join(tmpdir, "dir", ".dotfile"), content, 0600)
os.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)
extractionDir := t.TempDir()
cmd := exec.Command("tar", "-xzf", gzPath, "-C", extractionDir)
err = cmd.Run()
if err != nil {
@@ -53,7 +47,7 @@ func Test_shouldCreateArhive(t *testing.T) {
wasExtracted := func(p string) {
fullpath := path.Join(extractionDir, p)
assert.Contains(t, extractedFiles, fullpath)
copyContent, _ := ioutil.ReadFile(fullpath)
copyContent, _ := os.ReadFile(fullpath)
assert.Equal(t, content, copyContent)
}
@@ -63,22 +57,18 @@ func Test_shouldCreateArhive(t *testing.T) {
}
func Test_shouldCreateArhiveXXXXX(t *testing.T) {
tmpdir, _ := ioutils.TempDir("", "backup")
defer os.RemoveAll(tmpdir)
tmpdir := t.TempDir()
content := []byte("content")
ioutil.WriteFile(path.Join(tmpdir, "outer"), content, 0600)
os.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)
os.WriteFile(path.Join(tmpdir, "dir", ".dotfile"), content, 0600)
os.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)
extractionDir := t.TempDir()
r, _ := os.Open(gzPath)
ExtractTarGz(r, extractionDir)
if err != nil {
@@ -89,7 +79,7 @@ func Test_shouldCreateArhiveXXXXX(t *testing.T) {
wasExtracted := func(p string) {
fullpath := path.Join(extractionDir, p)
assert.Contains(t, extractedFiles, fullpath)
copyContent, _ := ioutil.ReadFile(fullpath)
copyContent, _ := os.ReadFile(fullpath)
assert.Equal(t, content, copyContent)
}

View File

@@ -4,12 +4,12 @@ import (
"archive/zip"
"bytes"
"fmt"
"github.com/pkg/errors"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/pkg/errors"
)
// UnzipArchive will unzip an archive from bytes into the dest destination folder on disk
@@ -36,7 +36,7 @@ func extractFileFromArchive(file *zip.File, dest string) error {
}
defer f.Close()
data, err := ioutil.ReadAll(f)
data, err := io.ReadAll(f)
if err != nil {
return err
}

View File

@@ -1,17 +1,14 @@
package archive
import (
"github.com/stretchr/testify/assert"
"io/ioutil"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
)
func TestUnzipFile(t *testing.T) {
dir, err := ioutil.TempDir("", "unzip-test-")
assert.NoError(t, err)
defer os.RemoveAll(dir)
dir := t.TempDir()
/*
Archive structure.
├── 0
@@ -21,7 +18,7 @@ func TestUnzipFile(t *testing.T) {
└── 0.txt
*/
err = UnzipFile("./testdata/sample_archive.zip", dir)
err := UnzipFile("./testdata/sample_archive.zip", dir)
assert.NoError(t, err)
archiveDir := dir + "/sample_archive"

View File

@@ -7,16 +7,17 @@ import (
"path/filepath"
"time"
"github.com/pkg/errors"
"github.com/portainer/portainer/api/archive"
"github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/filesystem"
"github.com/portainer/portainer/api/http/offlinegate"
"github.com/sirupsen/logrus"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)
const rwxr__r__ os.FileMode = 0744
const rwxr__r__ os.FileMode = 0o744
var filesToBackup = []string{
"certs",
@@ -29,6 +30,7 @@ var filesToBackup = []string{
"portainer.key",
"portainer.pub",
"tls",
"chisel",
}
// Creates a tar.gz system archive and encrypts it if password is not empty. Returns a path to the archive file.
@@ -47,9 +49,9 @@ func CreateBackupArchive(password string, gate *offlinegate.OfflineGate, datasto
err := datastore.Export(exportFilename)
if err != nil {
logrus.WithError(err).Debugf("failed to export to %s", exportFilename)
log.Error().Err(err).Str("filename", exportFilename).Msg("failed to export")
} else {
logrus.Debugf("exported to %s", exportFilename)
log.Debug().Str("filename", exportFilename).Msg("file exported")
}
}
@@ -80,14 +82,9 @@ func CreateBackupArchive(password string, gate *offlinegate.OfflineGate, datasto
}
func backupDb(backupDirPath string, datastore dataservices.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()
dbFileName := datastore.Connection().GetDatabaseFileName()
_, err := datastore.Backup(filepath.Join(backupDirPath, dbFileName))
return err
}
func encrypt(path string, passphrase string) (string, error) {

View File

@@ -3,8 +3,10 @@ package backup
import (
"context"
"io"
"io/fs"
"os"
"path/filepath"
"regexp"
"time"
"github.com/pkg/errors"
@@ -24,7 +26,7 @@ func RestoreArchive(archive io.Reader, password string, filestorePath string, ga
if password != "" {
archive, err = decrypt(archive, password)
if err != nil {
return errors.Wrap(err, "failed to decrypt the archive")
return errors.Wrap(err, "failed to decrypt the archive. Please ensure the password is correct and try again")
}
}
@@ -43,6 +45,12 @@ func RestoreArchive(archive io.Reader, password string, filestorePath string, ga
return errors.Wrap(err, "Failed to stop db")
}
// At some point, backups were created containing a subdirectory, now we need to handle both
restorePath, err = getRestoreSourcePath(restorePath)
if err != nil {
return errors.Wrap(err, "failed to restore from backup. Portainer database missing from backup file")
}
if err = restoreFiles(restorePath, filestorePath); err != nil {
return errors.Wrap(err, "failed to restore the system state")
}
@@ -59,6 +67,26 @@ func extractArchive(r io.Reader, destinationDirPath string) error {
return archive.ExtractTarGz(r, destinationDirPath)
}
func getRestoreSourcePath(dir string) (string, error) {
// find portainer.db or portainer.edb file. Return the parent directory
var portainerdbRegex = regexp.MustCompile(`^portainer.e?db$`)
backupDirPath := dir
err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if portainerdbRegex.MatchString(d.Name()) {
backupDirPath = filepath.Dir(path)
return filepath.SkipDir
}
return nil
})
return backupDirPath, err
}
func restoreFiles(srcDir string, destinationDir string) error {
for _, filename := range filesToRestore {
err := filesystem.CopyPath(filepath.Join(srcDir, filename), destinationDir)

12
api/build/variables.go Normal file
View File

@@ -0,0 +1,12 @@
package build
import "runtime"
// Variables to be set during the build time
var BuildNumber string
var ImageTag string
var NodejsVersion string
var YarnVersion string
var WebpackVersion string
var GoVersion string = runtime.Version()
var GitCommit string

View File

@@ -0,0 +1,61 @@
package crypto
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"math/big"
chshare "github.com/jpillora/chisel/share"
)
var one = new(big.Int).SetInt64(1)
// GenerateGo119CompatibleKey This function is basically copied from chshare.GenerateKey.
func GenerateGo119CompatibleKey(seed string) ([]byte, error) {
r := chshare.NewDetermRand([]byte(seed))
priv, err := ecdsaGenerateKey(elliptic.P256(), r)
if err != nil {
return nil, err
}
b, err := x509.MarshalECPrivateKey(priv)
if err != nil {
return nil, fmt.Errorf("Unable to marshal ECDSA private key: %w", err)
}
return pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: b}), nil
}
// This function is copied from Go1.19
func randFieldElement(c elliptic.Curve, rand io.Reader) (k *big.Int, err error) {
params := c.Params()
// Note that for P-521 this will actually be 63 bits more than the order, as
// division rounds down, but the extra bit is inconsequential.
b := make([]byte, params.N.BitLen()/8+8)
_, err = io.ReadFull(rand, b)
if err != nil {
return
}
k = new(big.Int).SetBytes(b)
n := new(big.Int).Sub(params.N, one)
k.Mod(k, n)
k.Add(k, one)
return
}
// This function is copied from Go1.19
func ecdsaGenerateKey(c elliptic.Curve, rand io.Reader) (*ecdsa.PrivateKey, error) {
k, err := randFieldElement(c, rand)
if err != nil {
return nil, err
}
priv := new(ecdsa.PrivateKey)
priv.PublicKey.Curve = c
priv.D = k
priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes())
return priv, nil
}

View File

@@ -0,0 +1,37 @@
package crypto
import (
"reflect"
"testing"
)
func TestGenerateGo119CompatibleKey(t *testing.T) {
type args struct {
seed string
}
tests := []struct {
name string
args args
want []byte
wantErr bool
}{
{
name: "Generate Go 1.19 compatible private key with a given seed",
args: args{seed: "94qh17MCIk8BOkiI"},
want: []byte("-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIHeohwk0Gy3RHVVViaHz7pz/HOiqA7fkv1FTM3mGgfT3oAoGCCqGSM49\nAwEHoUQDQgAEN7riX06xDsLNPuUmOvYFluNEakcFwZZRVvOcIYk/9VYnanDzW0Km\n8/BUUiKyJDuuGdS4fj9SlQ4iL8yBK01uKg==\n-----END EC PRIVATE KEY-----\n"),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GenerateGo119CompatibleKey(tt.args.seed)
if (err != nil) != tt.wantErr {
t.Errorf("GenerateGo119CompatibleKey() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GenerateGo119CompatibleKey()\ngot: Z %v\nwant: %v", got, tt.want)
}
})
}
}

View File

@@ -2,46 +2,81 @@ package chisel
import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/internal/edge/cache"
)
// EdgeJobs retrieves the edge jobs for the given environment
func (service *Service) EdgeJobs(endpointID portainer.EndpointID) []portainer.EdgeJob {
service.mu.RLock()
defer service.mu.RUnlock()
return append(
make([]portainer.EdgeJob, 0, len(service.edgeJobs[endpointID])),
service.edgeJobs[endpointID]...,
)
}
// AddEdgeJob register an EdgeJob inside the tunnel details associated to an environment(endpoint).
func (service *Service) AddEdgeJob(endpointID portainer.EndpointID, edgeJob *portainer.EdgeJob) {
func (service *Service) AddEdgeJob(endpoint *portainer.Endpoint, edgeJob *portainer.EdgeJob) {
if endpoint.Edge.AsyncMode {
return
}
service.mu.Lock()
tunnel := service.getTunnelDetails(endpointID)
defer service.mu.Unlock()
existingJobIndex := -1
for idx, existingJob := range tunnel.Jobs {
for idx, existingJob := range service.edgeJobs[endpoint.ID] {
if existingJob.ID == edgeJob.ID {
existingJobIndex = idx
break
}
}
if existingJobIndex == -1 {
tunnel.Jobs = append(tunnel.Jobs, *edgeJob)
service.edgeJobs[endpoint.ID] = append(service.edgeJobs[endpoint.ID], *edgeJob)
} else {
tunnel.Jobs[existingJobIndex] = *edgeJob
service.edgeJobs[endpoint.ID][existingJobIndex] = *edgeJob
}
service.mu.Unlock()
cache.Del(endpoint.ID)
}
// RemoveEdgeJob will remove the specified Edge job from each tunnel it was registered with.
func (service *Service) RemoveEdgeJob(edgeJobID portainer.EdgeJobID) {
service.mu.Lock()
for _, tunnel := range service.tunnelDetailsMap {
// Filter in-place
for endpointID := range service.edgeJobs {
n := 0
for _, edgeJob := range tunnel.Jobs {
for _, edgeJob := range service.edgeJobs[endpointID] {
if edgeJob.ID != edgeJobID {
tunnel.Jobs[n] = edgeJob
service.edgeJobs[endpointID][n] = edgeJob
n++
}
}
tunnel.Jobs = tunnel.Jobs[:n]
service.edgeJobs[endpointID] = service.edgeJobs[endpointID][:n]
cache.Del(endpointID)
}
service.mu.Unlock()
}
func (service *Service) RemoveEdgeJobFromEndpoint(endpointID portainer.EndpointID, edgeJobID portainer.EdgeJobID) {
service.mu.Lock()
defer service.mu.Unlock()
n := 0
for _, edgeJob := range service.edgeJobs[endpointID] {
if edgeJob.ID != edgeJobID {
service.edgeJobs[endpointID][n] = edgeJob
n++
}
}
service.edgeJobs[endpointID] = service.edgeJobs[endpointID][:n]
cache.Del(endpointID)
}

View File

@@ -3,91 +3,143 @@ package chisel
import (
"context"
"fmt"
"log"
"io"
"net/http"
"sync"
"time"
chserver "github.com/andres-portainer/chisel/server"
"github.com/dchest/uniuri"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/proxy"
chserver "github.com/jpillora/chisel/server"
"github.com/jpillora/chisel/share/ccrypto"
"github.com/rs/zerolog/log"
)
const (
tunnelCleanupInterval = 10 * time.Second
requiredTimeout = 15 * time.Second
activeTimeout = 4*time.Minute + 30*time.Second
pingTimeout = 3 * 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 map[portainer.EndpointID]*portainer.TunnelDetails
dataStore dataservices.DataStore
snapshotService portainer.SnapshotService
chiselServer *chserver.Server
shutdownCtx context.Context
ProxyManager *proxy.Manager
mu sync.Mutex
serverFingerprint string
serverPort string
activeTunnels map[portainer.EndpointID]*portainer.TunnelDetails
edgeJobs map[portainer.EndpointID][]portainer.EdgeJob
dataStore dataservices.DataStore
snapshotService portainer.SnapshotService
chiselServer *chserver.Server
shutdownCtx context.Context
ProxyManager *proxy.Manager
mu sync.RWMutex
fileService portainer.FileService
defaultCheckinInterval int
}
// NewService returns a pointer to a new instance of Service
func NewService(dataStore dataservices.DataStore, shutdownCtx context.Context) *Service {
func NewService(dataStore dataservices.DataStore, shutdownCtx context.Context, fileService portainer.FileService) *Service {
defaultCheckinInterval := portainer.DefaultEdgeAgentCheckinIntervalInSeconds
settings, err := dataStore.Settings().Settings()
if err == nil {
defaultCheckinInterval = settings.EdgeAgentCheckinInterval
} else {
log.Error().Err(err).Msg("unable to retrieve the settings from the database")
}
return &Service{
tunnelDetailsMap: make(map[portainer.EndpointID]*portainer.TunnelDetails),
dataStore: dataStore,
shutdownCtx: shutdownCtx,
activeTunnels: make(map[portainer.EndpointID]*portainer.TunnelDetails),
edgeJobs: make(map[portainer.EndpointID][]portainer.EdgeJob),
dataStore: dataStore,
shutdownCtx: shutdownCtx,
fileService: fileService,
defaultCheckinInterval: defaultCheckinInterval,
}
}
// pingAgent ping the given agent so that the agent can keep the tunnel alive
func (service *Service) pingAgent(endpointID portainer.EndpointID) error {
tunnel := service.GetTunnelDetails(endpointID)
requestURL := fmt.Sprintf("http://127.0.0.1:%d/ping", tunnel.Port)
endpoint, err := service.dataStore.Endpoint().Endpoint(endpointID)
if err != nil {
return err
}
tunnelAddr, err := service.TunnelAddr(endpoint)
if err != nil {
return err
}
requestURL := fmt.Sprintf("http://%s/ping", tunnelAddr)
req, err := http.NewRequest(http.MethodHead, requestURL, nil)
if err != nil {
return err
}
httpClient := &http.Client{
Timeout: 3 * time.Second,
Timeout: pingTimeout,
}
_, err = httpClient.Do(req)
return err
resp, err := httpClient.Do(req)
if err != nil {
return err
}
io.Copy(io.Discard, resp.Body)
resp.Body.Close()
return nil
}
// KeepTunnelAlive keeps the tunnel of the given environment for maxAlive duration, or until ctx is done
func (service *Service) KeepTunnelAlive(endpointID portainer.EndpointID, ctx context.Context, maxAlive time.Duration) {
go func() {
log.Printf("[DEBUG] [chisel,KeepTunnelAlive] [endpoint_id: %d] [message: start for %.0f minutes]\n", endpointID, maxAlive.Minutes())
maxAliveTicker := time.NewTicker(maxAlive)
defer maxAliveTicker.Stop()
pingTicker := time.NewTicker(tunnelCleanupInterval)
defer pingTicker.Stop()
go service.keepTunnelAlive(endpointID, ctx, maxAlive)
}
for {
select {
case <-pingTicker.C:
service.SetTunnelStatusToActive(endpointID)
err := service.pingAgent(endpointID)
if err != nil {
log.Printf("[DEBUG] [chisel,KeepTunnelAlive] [endpoint_id: %d] [warning: ping agent err=%s]\n", endpointID, err)
}
case <-maxAliveTicker.C:
log.Printf("[DEBUG] [chisel,KeepTunnelAlive] [endpoint_id: %d] [message: stop as %.0f minutes timeout]\n", endpointID, maxAlive.Minutes())
return
case <-ctx.Done():
err := ctx.Err()
log.Printf("[DEBUG] [chisel,KeepTunnelAlive] [endpoint_id: %d] [message: stop as err=%s]\n", endpointID, err)
return
func (service *Service) keepTunnelAlive(endpointID portainer.EndpointID, ctx context.Context, maxAlive time.Duration) {
log.Debug().
Int("endpoint_id", int(endpointID)).
Float64("max_alive_minutes", maxAlive.Minutes()).
Msg("KeepTunnelAlive: start")
maxAliveTicker := time.NewTicker(maxAlive)
defer maxAliveTicker.Stop()
pingTicker := time.NewTicker(tunnelCleanupInterval)
defer pingTicker.Stop()
for {
select {
case <-pingTicker.C:
service.UpdateLastActivity(endpointID)
if err := service.pingAgent(endpointID); err != nil {
log.Debug().
Int("endpoint_id", int(endpointID)).
Err(err).
Msg("KeepTunnelAlive: ping agent")
}
case <-maxAliveTicker.C:
log.Debug().
Int("endpoint_id", int(endpointID)).
Float64("timeout_minutes", maxAlive.Minutes()).
Msg("KeepTunnelAlive: tunnel keep alive timeout")
return
case <-ctx.Done():
err := ctx.Err()
log.Debug().
Int("endpoint_id", int(endpointID)).
Err(err).
Msg("KeepTunnelAlive: tunnel stop")
return
}
}()
}
}
// StartTunnelServer starts a tunnel server on the specified addr and port.
@@ -96,14 +148,14 @@ func (service *Service) KeepTunnelAlive(endpointID portainer.EndpointID, ctx con
// 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()
privateKeyFile, err := service.retrievePrivateKeyFile()
if err != nil {
return err
}
config := &chserver.Config{
Reverse: true,
KeySeed: keySeed,
KeyFile: privateKeyFile,
}
chiselServer, err := chserver.NewServer(config)
@@ -114,21 +166,21 @@ func (service *Service) StartTunnelServer(addr, port string, snapshotService por
service.serverFingerprint = chiselServer.GetFingerprint()
service.serverPort = port
err = chiselServer.Start(addr, port)
if err != nil {
if err := chiselServer.Start(addr, port); 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 {
if err = service.chiselServer.AddUser(username, password, "127.0.0.1"); err != nil {
return err
}
service.snapshotService = snapshotService
go service.startTunnelVerificationLoop()
return nil
@@ -139,30 +191,50 @@ func (service *Service) StopTunnelServer() error {
return service.chiselServer.Close()
}
func (service *Service) retrievePrivateKeySeed() (string, error) {
var serverInfo *portainer.TunnelServerInfo
func (service *Service) retrievePrivateKeyFile() (string, error) {
privateKeyFile := service.fileService.GetDefaultChiselPrivateKeyPath()
serverInfo, err := service.dataStore.TunnelServer().Info()
if service.dataStore.IsErrObjectNotFound(err) {
keySeed := uniuri.NewLen(16)
if exists, _ := service.fileService.FileExists(privateKeyFile); exists {
log.Info().
Str("private-key", privateKeyFile).
Msg("found Chisel private key file on disk")
serverInfo = &portainer.TunnelServerInfo{
PrivateKeySeed: keySeed,
}
return privateKeyFile, nil
}
log.Debug().
Str("private-key", privateKeyFile).
Msg("chisel private key file does not exist")
privateKey, err := ccrypto.GenerateKey("")
if err != nil {
log.Error().
Err(err).
Msg("failed to generate chisel private key")
err := service.dataStore.TunnelServer().UpdateInfo(serverInfo)
if err != nil {
return "", err
}
} else if err != nil {
return "", err
}
return serverInfo.PrivateKeySeed, nil
if err = service.fileService.StoreChiselPrivateKey(privateKey); err != nil {
log.Error().
Err(err).
Msg("failed to save Chisel private key to disk")
return "", err
}
log.Info().
Str("private-key", privateKeyFile).
Msg("generated a new Chisel private key file")
return privateKeyFile, nil
}
func (service *Service) startTunnelVerificationLoop() {
log.Printf("[DEBUG] [chisel, monitoring] [check_interval_seconds: %f] [message: starting tunnel management process]", tunnelCleanupInterval.Seconds())
log.Debug().
Float64("check_interval_seconds", tunnelCleanupInterval.Seconds()).
Msg("starting tunnel management process")
ticker := time.NewTicker(tunnelCleanupInterval)
for {
@@ -170,52 +242,57 @@ func (service *Service) startTunnelVerificationLoop() {
case <-ticker.C:
service.checkTunnels()
case <-service.shutdownCtx.Done():
log.Println("[DEBUG] Shutting down tunnel service")
log.Debug().Msg("shutting down tunnel service")
if err := service.StopTunnelServer(); err != nil {
log.Printf("Stopped tunnel service: %s", err)
log.Debug().Err(err).Msg("stopped tunnel service")
}
ticker.Stop()
return
}
}
}
// checkTunnels finds the first tunnel that has not had any activity recently
// and attempts to take a snapshot, then closes it and returns
func (service *Service) checkTunnels() {
tunnels := make(map[portainer.EndpointID]portainer.TunnelDetails)
service.mu.Lock()
for key, tunnel := range service.tunnelDetailsMap {
tunnels[key] = *tunnel
}
service.mu.Unlock()
for endpointID, tunnel := range tunnels {
if tunnel.LastActivity.IsZero() || tunnel.Status == portainer.EdgeAgentIdle {
continue
}
service.mu.RLock()
for endpointID, tunnel := range service.activeTunnels {
elapsed := time.Since(tunnel.LastActivity)
log.Printf("[DEBUG] [chisel,monitoring] [endpoint_id: %d] [status: %s] [status_time_seconds: %f] [message: environment tunnel monitoring]", endpointID, tunnel.Status, elapsed.Seconds())
log.Debug().
Int("endpoint_id", int(endpointID)).
Float64("last_activity_seconds", elapsed.Seconds()).
Msg("environment tunnel monitoring")
if tunnel.Status == portainer.EdgeAgentManagementRequired && elapsed.Seconds() < requiredTimeout.Seconds() {
if tunnel.Status == portainer.EdgeAgentManagementRequired && elapsed < activeTimeout {
continue
} else if tunnel.Status == portainer.EdgeAgentManagementRequired && elapsed.Seconds() > requiredTimeout.Seconds() {
log.Printf("[DEBUG] [chisel,monitoring] [endpoint_id: %d] [status: %s] [status_time_seconds: %f] [timeout_seconds: %f] [message: REQUIRED state timeout exceeded]", endpointID, 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: %d] [status: %s] [status_time_seconds: %f] [timeout_seconds: %f] [message: ACTIVE state timeout exceeded]", endpointID, tunnel.Status, elapsed.Seconds(), activeTimeout.Seconds())
tunnelPort := tunnel.Port
err := service.snapshotEnvironment(endpointID, tunnel.Port)
if err != nil {
log.Printf("[ERROR] [snapshot] Unable to snapshot Edge environment (id: %d): %s", endpointID, err)
}
service.mu.RUnlock()
log.Debug().
Int("endpoint_id", int(endpointID)).
Float64("last_activity_seconds", elapsed.Seconds()).
Float64("timeout_seconds", activeTimeout.Seconds()).
Msg("last activity timeout exceeded")
if err := service.snapshotEnvironment(endpointID, tunnelPort); err != nil {
log.Error().
Int("endpoint_id", int(endpointID)).
Err(err).
Msg("unable to snapshot Edge environment")
}
service.SetTunnelStatusToIdle(portainer.EndpointID(endpointID))
service.close(endpointID)
return
}
service.mu.RUnlock()
}
func (service *Service) snapshotEnvironment(endpointID portainer.EndpointID, tunnelPort int) error {
@@ -224,14 +301,7 @@ func (service *Service) snapshotEnvironment(endpointID portainer.EndpointID, tun
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)
return service.snapshotService.SnapshotEndpoint(endpoint)
}

View File

@@ -0,0 +1,54 @@
package chisel
import (
"context"
"net"
"net/http"
"testing"
"time"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/datastore"
"github.com/stretchr/testify/require"
)
func TestPingAgentPanic(t *testing.T) {
endpoint := &portainer.Endpoint{
ID: 1,
EdgeID: "test-edge-id",
Type: portainer.EdgeAgentOnDockerEnvironment,
UserTrusted: true,
}
_, store := datastore.MustNewTestStore(t, true, true)
s := NewService(store, nil, nil)
defer func() {
require.Nil(t, recover())
}()
mux := http.NewServeMux()
mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
time.Sleep(pingTimeout + 1*time.Second)
})
ln, err := net.ListenTCP("tcp", &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 0})
require.NoError(t, err)
srv := &http.Server{Handler: mux}
errCh := make(chan error)
go func() {
errCh <- srv.Serve(ln)
}()
err = s.Open(endpoint)
require.NoError(t, err)
s.activeTunnels[endpoint.ID].Port = ln.Addr().(*net.TCPAddr).Port
require.Error(t, s.pingAgent(endpoint.ID))
require.NoError(t, srv.Shutdown(context.Background()))
require.ErrorIs(t, <-errCh, http.ErrServerClosed)
}

View File

@@ -2,14 +2,21 @@ package chisel
import (
"encoding/base64"
"errors"
"fmt"
"math/rand"
"net"
"strings"
"time"
"github.com/dchest/uniuri"
"github.com/portainer/libcrypto"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/internal/edge"
"github.com/portainer/portainer/api/internal/edge/cache"
"github.com/portainer/portainer/api/internal/endpointutils"
"github.com/portainer/portainer/pkg/libcrypto"
"github.com/dchest/uniuri"
"github.com/rs/zerolog/log"
)
const (
@@ -17,18 +24,191 @@ const (
maxAvailablePort = 65535
)
var (
ErrNonEdgeEnv = errors.New("cannot open a tunnel for non-edge environments")
ErrAsyncEnv = errors.New("cannot open a tunnel for async edge environments")
ErrInvalidEnv = errors.New("cannot open a tunnel for an invalid environment")
)
// Open will mark the tunnel as REQUIRED so the agent opens it
func (s *Service) Open(endpoint *portainer.Endpoint) error {
if !endpointutils.IsEdgeEndpoint(endpoint) {
return ErrNonEdgeEnv
}
if endpoint.Edge.AsyncMode {
return ErrAsyncEnv
}
if endpoint.ID == 0 || endpoint.EdgeID == "" || !endpoint.UserTrusted {
return ErrInvalidEnv
}
s.mu.Lock()
defer s.mu.Unlock()
if _, ok := s.activeTunnels[endpoint.ID]; ok {
return nil
}
defer cache.Del(endpoint.ID)
tun := &portainer.TunnelDetails{
Status: portainer.EdgeAgentManagementRequired,
Port: s.getUnusedPort(),
LastActivity: time.Now(),
}
username, password := generateRandomCredentials()
if s.chiselServer != nil {
authorizedRemote := fmt.Sprintf("^R:0.0.0.0:%d$", tun.Port)
if err := s.chiselServer.AddUser(username, password, authorizedRemote); err != nil {
return err
}
}
credentials, err := encryptCredentials(username, password, endpoint.EdgeID)
if err != nil {
return err
}
tun.Credentials = credentials
s.activeTunnels[endpoint.ID] = tun
return nil
}
// close removes the tunnel from the map so the agent will close it
func (s *Service) close(endpointID portainer.EndpointID) {
s.mu.Lock()
defer s.mu.Unlock()
tun, ok := s.activeTunnels[endpointID]
if !ok {
return
}
if len(tun.Credentials) > 0 && s.chiselServer != nil {
user, _, _ := strings.Cut(tun.Credentials, ":")
s.chiselServer.DeleteUser(user)
}
if s.ProxyManager != nil {
s.ProxyManager.DeleteEndpointProxy(endpointID)
}
delete(s.activeTunnels, endpointID)
cache.Del(endpointID)
}
// Config returns the tunnel details needed for the agent to connect
func (s *Service) Config(endpointID portainer.EndpointID) portainer.TunnelDetails {
s.mu.RLock()
defer s.mu.RUnlock()
if tun, ok := s.activeTunnels[endpointID]; ok {
return *tun
}
return portainer.TunnelDetails{Status: portainer.EdgeAgentIdle}
}
// TunnelAddr returns the address of the local tunnel, including the port, it
// will block until the tunnel is ready
func (s *Service) TunnelAddr(endpoint *portainer.Endpoint) (string, error) {
if err := s.Open(endpoint); err != nil {
return "", err
}
tun := s.Config(endpoint.ID)
checkinInterval := time.Duration(s.tryEffectiveCheckinInterval(endpoint)) * time.Second
for t0 := time.Now(); ; {
if time.Since(t0) > 2*checkinInterval {
s.close(endpoint.ID)
return "", errors.New("unable to open the tunnel")
}
// Check if the tunnel is established
conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: tun.Port})
if err != nil {
time.Sleep(checkinInterval / 100)
continue
}
conn.Close()
break
}
s.UpdateLastActivity(endpoint.ID)
return fmt.Sprintf("127.0.0.1:%d", tun.Port), nil
}
// tryEffectiveCheckinInterval avoids a potential deadlock by returning a
// previous known value after a timeout
func (s *Service) tryEffectiveCheckinInterval(endpoint *portainer.Endpoint) int {
ch := make(chan int, 1)
go func() {
ch <- edge.EffectiveCheckinInterval(s.dataStore, endpoint)
}()
select {
case <-time.After(50 * time.Millisecond):
s.mu.RLock()
defer s.mu.RUnlock()
return s.defaultCheckinInterval
case i := <-ch:
s.mu.Lock()
s.defaultCheckinInterval = i
s.mu.Unlock()
return i
}
}
// UpdateLastActivity sets the current timestamp to avoid the tunnel timeout
func (s *Service) UpdateLastActivity(endpointID portainer.EndpointID) {
s.mu.Lock()
defer s.mu.Unlock()
if tun, ok := s.activeTunnels[endpointID]; ok {
tun.LastActivity = time.Now()
}
}
// NOTE: it needs to be called with the lock acquired
// 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 _, tunnel := range service.tunnelDetailsMap {
for _, tunnel := range service.activeTunnels {
if tunnel.Port == port {
return service.getUnusedPort()
}
}
conn, err := net.DialTCP("tcp", nil, &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: port})
if err == nil {
conn.Close()
log.Debug().
Int("port", port).
Msg("selected port is in use, trying a different one")
return service.getUnusedPort()
}
return port
}
@@ -36,134 +216,10 @@ func randomInt(min, max int) int {
return min + rand.Intn(max-min)
}
// NOTE: it needs to be called with the lock acquired
func (service *Service) getTunnelDetails(endpointID portainer.EndpointID) *portainer.TunnelDetails {
if tunnel, ok := service.tunnelDetailsMap[endpointID]; ok {
return tunnel
}
tunnel := &portainer.TunnelDetails{
Status: portainer.EdgeAgentIdle,
}
service.tunnelDetailsMap[endpointID] = tunnel
return tunnel
}
// GetTunnelDetails returns information about the tunnel associated to an environment(endpoint).
func (service *Service) GetTunnelDetails(endpointID portainer.EndpointID) portainer.TunnelDetails {
service.mu.Lock()
defer service.mu.Unlock()
return *service.getTunnelDetails(endpointID)
}
// GetActiveTunnel retrieves an active tunnel which allows communicating with edge agent
func (service *Service) GetActiveTunnel(endpoint *portainer.Endpoint) (portainer.TunnelDetails, error) {
tunnel := service.GetTunnelDetails(endpoint.ID)
if tunnel.Status == portainer.EdgeAgentActive {
// update the LastActivity
service.SetTunnelStatusToActive(endpoint.ID)
}
if tunnel.Status == portainer.EdgeAgentIdle || tunnel.Status == portainer.EdgeAgentManagementRequired {
err := service.SetTunnelStatusToRequired(endpoint.ID)
if err != nil {
return portainer.TunnelDetails{}, fmt.Errorf("failed opening tunnel to endpoint: %w", err)
}
if endpoint.EdgeCheckinInterval == 0 {
settings, err := service.dataStore.Settings().Settings()
if err != nil {
return portainer.TunnelDetails{}, fmt.Errorf("failed fetching settings from db: %w", err)
}
endpoint.EdgeCheckinInterval = settings.EdgeAgentCheckinInterval
}
time.Sleep(2 * time.Duration(endpoint.EdgeCheckinInterval) * time.Second)
}
return service.GetTunnelDetails(endpoint.ID), nil
}
// SetTunnelStatusToActive update the status of the tunnel associated to the specified environment(endpoint).
// It sets the status to ACTIVE.
func (service *Service) SetTunnelStatusToActive(endpointID portainer.EndpointID) {
service.mu.Lock()
tunnel := service.getTunnelDetails(endpointID)
tunnel.Status = portainer.EdgeAgentActive
tunnel.Credentials = ""
tunnel.LastActivity = time.Now()
service.mu.Unlock()
}
// SetTunnelStatusToIdle update the status of the tunnel associated to the specified environment(endpoint).
// It sets the status to IDLE.
// It removes any existing credentials associated to the tunnel.
func (service *Service) SetTunnelStatusToIdle(endpointID portainer.EndpointID) {
service.mu.Lock()
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])
}
service.ProxyManager.DeleteEndpointProxy(endpointID)
service.mu.Unlock()
}
// SetTunnelStatusToRequired update the status of the tunnel associated to the specified environment(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 environment(endpoint).
func (service *Service) SetTunnelStatusToRequired(endpointID portainer.EndpointID) error {
tunnel := service.getTunnelDetails(endpointID)
service.mu.Lock()
defer service.mu.Unlock()
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
}
return nil
}
func generateRandomCredentials() (string, string) {
username := uniuri.NewLen(8)
password := uniuri.NewLen(8)
return username, password
}

View File

@@ -2,15 +2,14 @@ package cli
import (
"errors"
"log"
"os"
"path/filepath"
"strings"
"time"
portainer "github.com/portainer/portainer/api"
"os"
"path/filepath"
"strings"
"github.com/rs/zerolog/log"
"gopkg.in/alecthomas/kingpin.v2"
)
@@ -18,17 +17,14 @@ import (
type Service struct{}
var (
errInvalidEndpointProtocol = errors.New("Invalid environment 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")
ErrInvalidEndpointProtocol = errors.New("Invalid environment 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
func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
kingpin.Version(version)
flags := &portainer.CLIFlags{
func CLIFlags() *portainer.CLIFlags {
return &portainer.CLIFlags{
Addr: kingpin.Flag("bind", "Address and port to serve Portainer").Default(defaultBindAddress).Short('p').String(),
AddrHTTPS: kingpin.Flag("bind-https", "Address and port to serve Portainer via https").Default(defaultHTTPSBindAddress).String(),
TunnelAddr: kingpin.Flag("tunnel-addr", "Address to serve the tunnel server").Default(defaultTunnelServerAddress).String(),
@@ -36,7 +32,7 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
Assets: kingpin.Flag("assets", "Path to the assets").Default(defaultAssetsDirectory).Short('a').String(),
Data: kingpin.Flag("data", "Path to the folder where the data is stored").Default(defaultDataDirectory).Short('d').String(),
EndpointURL: kingpin.Flag("host", "Environment URL").Short('H').String(),
FeatureFlags: BoolPairs(kingpin.Flag("feat", "List of feature flags").Hidden()),
FeatureFlags: kingpin.Flag("feat", "List of feature flags").Strings(),
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(),
@@ -49,9 +45,9 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
SSL: kingpin.Flag("ssl", "Secure Portainer instance using SSL (deprecated)").Default(defaultSSL).Bool(),
SSLCert: kingpin.Flag("sslcert", "Path to the SSL certificate used to secure the Portainer instance").String(),
SSLKey: kingpin.Flag("sslkey", "Path to the SSL key used to secure the Portainer instance").String(),
Rollback: kingpin.Flag("rollback", "Rollback the database store to the previous version").Bool(),
Rollback: kingpin.Flag("rollback", "Rollback the database to the previous backup").Bool(),
SnapshotInterval: kingpin.Flag("snapshot-interval", "Duration between each environment snapshot job").String(),
AdminPassword: kingpin.Flag("admin-password", "Hashed admin password").String(),
AdminPassword: kingpin.Flag("admin-password", "Set admin password with provided hash").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(),
@@ -61,7 +57,16 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
MaxBatchSize: kingpin.Flag("max-batch-size", "Maximum size of a batch").Int(),
MaxBatchDelay: kingpin.Flag("max-batch-delay", "Maximum delay before a batch starts").Duration(),
SecretKeyName: kingpin.Flag("secret-key-name", "Secret key name for encryption and will be used as /run/secrets/<secret-key-name>.").Default(defaultSecretKeyName).String(),
LogLevel: kingpin.Flag("log-level", "Set the minimum logging level to show").Default("INFO").Enum("DEBUG", "INFO", "WARN", "ERROR"),
LogMode: kingpin.Flag("log-mode", "Set the logging output mode").Default("PRETTY").Enum("NOCOLOR", "PRETTY", "JSON"),
}
}
// ParseFlags parse the CLI flags and return a portainer.Flags struct
func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
kingpin.Version(version)
flags := CLIFlags()
kingpin.Parse()
@@ -70,6 +75,7 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
if err != nil {
panic(err)
}
*flags.Assets = filepath.Join(filepath.Dir(ex), *flags.Assets)
}
@@ -78,21 +84,18 @@ func (*Service) ParseFlags(version string) (*portainer.CLIFlags, error) {
// ValidateFlags validates the values of the flags.
func (*Service) ValidateFlags(flags *portainer.CLIFlags) error {
displayDeprecationWarnings(flags)
err := validateEndpointURL(*flags.EndpointURL)
if err != nil {
if err := validateEndpointURL(*flags.EndpointURL); err != nil {
return err
}
err = validateSnapshotInterval(*flags.SnapshotInterval)
if err != nil {
if err := validateSnapshotInterval(*flags.SnapshotInterval); err != nil {
return err
}
if *flags.AdminPassword != "" && *flags.AdminPasswordFile != "" {
return errAdminPassExcludeAdminPassFile
return ErrAdminPassExcludeAdminPassFile
}
return nil
@@ -100,40 +103,47 @@ func (*Service) ValidateFlags(flags *portainer.CLIFlags) error {
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.")
log.Warn().Msg("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")
}
if *flags.SSL {
log.Println("Warning: SSL is enabled by default and there is no need for the --ssl flag. It has been kept to allow migration of instances running a previous version of Portainer with this flag enabled")
log.Warn().Msg("SSL is enabled by default and there is no need for the --ssl flag, it has been kept to allow migration of instances running a previous version of Portainer with this flag enabled")
}
}
func validateEndpointURL(endpointURL string) error {
if endpointURL != "" {
if !strings.HasPrefix(endpointURL, "unix://") && !strings.HasPrefix(endpointURL, "tcp://") && !strings.HasPrefix(endpointURL, "npipe://") {
return errInvalidEndpointProtocol
}
if endpointURL == "" {
return nil
}
if strings.HasPrefix(endpointURL, "unix://") || strings.HasPrefix(endpointURL, "npipe://") {
socketPath := strings.TrimPrefix(endpointURL, "unix://")
socketPath = strings.TrimPrefix(socketPath, "npipe://")
if _, err := os.Stat(socketPath); err != nil {
if os.IsNotExist(err) {
return errSocketOrNamedPipeNotFound
}
return err
if !strings.HasPrefix(endpointURL, "unix://") && !strings.HasPrefix(endpointURL, "tcp://") && !strings.HasPrefix(endpointURL, "npipe://") {
return ErrInvalidEndpointProtocol
}
if strings.HasPrefix(endpointURL, "unix://") || strings.HasPrefix(endpointURL, "npipe://") {
socketPath := strings.TrimPrefix(endpointURL, "unix://")
socketPath = strings.TrimPrefix(socketPath, "npipe://")
if _, err := os.Stat(socketPath); err != nil {
if os.IsNotExist(err) {
return ErrSocketOrNamedPipeNotFound
}
return err
}
}
return nil
}
func validateSnapshotInterval(snapshotInterval string) error {
if snapshotInterval != "" {
_, err := time.ParseDuration(snapshotInterval)
if err != nil {
return errInvalidSnapshotInterval
}
if snapshotInterval == "" {
return nil
}
if _, err := time.ParseDuration(snapshotInterval); err != nil {
return ErrInvalidSnapshotInterval
}
return nil
}

View File

@@ -2,23 +2,24 @@ package cli
import (
"bufio"
"log"
"fmt"
"os"
"strings"
)
// Confirm starts a rollback db cli application
func Confirm(message string) (bool, error) {
log.Printf("%s [y/N]", message)
fmt.Printf("%s [y/N] ", message)
reader := bufio.NewReader(os.Stdin)
answer, err := reader.ReadString('\n')
if err != nil {
return false, err
}
answer = strings.Replace(answer, "\n", "", -1)
answer = strings.ReplaceAll(answer, "\n", "")
answer = strings.ToLower(answer)
return answer == "y" || answer == "yes", nil
}

View File

@@ -18,8 +18,6 @@ const (
defaultHTTPDisabled = "false"
defaultHTTPEnabled = "false"
defaultSSL = "false"
defaultSSLCertPath = "/certs/portainer.crt"
defaultSSLKeyPath = "/certs/portainer.key"
defaultBaseURL = "/"
defaultSecretKeyName = "portainer"
)

View File

@@ -15,8 +15,6 @@ const (
defaultHTTPDisabled = "false"
defaultHTTPEnabled = "false"
defaultSSL = "false"
defaultSSLCertPath = "C:\\certs\\portainer.crt"
defaultSSLKeyPath = "C:\\certs\\portainer.key"
defaultSnapshotInterval = "5m"
defaultBaseURL = "/"
defaultSecretKeyName = "portainer"

View File

@@ -1,10 +1,10 @@
package cli
import (
portainer "github.com/portainer/portainer/api"
"strings"
portainer "github.com/portainer/portainer/api"
"gopkg.in/alecthomas/kingpin.v2"
)

View File

@@ -1,29 +0,0 @@
package main
import (
"log"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/datastore"
"github.com/sirupsen/logrus"
)
func importFromJson(fileService portainer.FileService, store *datastore.Store) {
// EXPERIMENTAL - if used with an incomplete json file, it will fail, as we don't have a way to default the model values
importFile := "/data/import.json"
if exists, _ := fileService.FileExists(importFile); exists {
if err := store.Import(importFile); err != nil {
logrus.WithError(err).Debugf("Import %s failed", importFile)
// TODO: should really rollback on failure, but then we have nothing.
} else {
logrus.Printf("Successfully imported %s to new portainer database", importFile)
}
// TODO: this is bad - its to ensure that any defaults that were broken in import, or migrations get set back to what we want
// I also suspect that everything from "Init to Init" is potentially a migration
err := store.Init()
if err != nil {
log.Fatalf("Failed initializing data store: %v", err)
}
}
}

View File

@@ -2,41 +2,62 @@ package main
import (
"fmt"
"log"
"strings"
stdlog "log"
"os"
"github.com/sirupsen/logrus"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/rs/zerolog/pkgerrors"
)
type portainerFormatter struct {
logrus.TextFormatter
}
func (f *portainerFormatter) Format(entry *logrus.Entry) ([]byte, error) {
var levelColor int
switch entry.Level {
case logrus.DebugLevel, logrus.TraceLevel:
levelColor = 31 // gray
case logrus.WarnLevel:
levelColor = 33 // yellow
case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
levelColor = 31 // red
default:
levelColor = 36 // blue
}
return []byte(fmt.Sprintf("\x1b[%dm%s\x1b[0m %s %s\n", levelColor, strings.ToUpper(entry.Level.String()), entry.Time.Format(f.TimestampFormat), entry.Message)), nil
}
func configureLogger() {
logger := logrus.New() // logger is to implicitly substitute stdlib's log
log.SetOutput(logger.Writer())
zerolog.ErrorStackFieldName = "stack_trace"
zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
formatter := &logrus.TextFormatter{DisableTimestamp: true, DisableLevelTruncation: true}
formatterLogrus := &portainerFormatter{logrus.TextFormatter{DisableTimestamp: false, DisableLevelTruncation: true, TimestampFormat: "2006/01/02 15:04:05", FullTimestamp: true}}
stdlog.SetFlags(0)
stdlog.SetOutput(log.Logger)
logger.SetFormatter(formatter)
logrus.SetFormatter(formatterLogrus)
logger.SetLevel(logrus.DebugLevel)
logrus.SetLevel(logrus.DebugLevel)
log.Logger = log.Logger.With().Caller().Stack().Logger()
}
func setLoggingLevel(level string) {
switch level {
case "ERROR":
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
case "WARN":
zerolog.SetGlobalLevel(zerolog.WarnLevel)
case "INFO":
zerolog.SetGlobalLevel(zerolog.InfoLevel)
case "DEBUG":
zerolog.SetGlobalLevel(zerolog.DebugLevel)
}
}
func setLoggingMode(mode string) {
switch mode {
case "PRETTY":
log.Logger = log.Output(zerolog.ConsoleWriter{
Out: os.Stderr,
TimeFormat: "2006/01/02 03:04PM",
FormatMessage: formatMessage,
})
case "NOCOLOR":
log.Logger = log.Output(zerolog.ConsoleWriter{
Out: os.Stderr,
TimeFormat: "2006/01/02 03:04PM",
FormatMessage: formatMessage,
NoColor: true,
})
case "JSON":
log.Logger = log.Output(os.Stderr)
}
}
func formatMessage(i any) string {
if i == nil {
return ""
}
return fmt.Sprintf("%s |", i)
}

View File

@@ -1,76 +1,90 @@
package main
import (
"cmp"
"context"
"crypto/sha256"
"fmt"
"log"
"os"
"path"
"strconv"
"strings"
"time"
"github.com/sirupsen/logrus"
"github.com/portainer/libhelm"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/apikey"
"github.com/portainer/portainer/api/build"
"github.com/portainer/portainer/api/chisel"
"github.com/portainer/portainer/api/cli"
"github.com/portainer/portainer/api/crypto"
"github.com/portainer/portainer/api/database"
"github.com/portainer/portainer/api/database/boltdb"
"github.com/portainer/portainer/api/database/models"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/datastore"
"github.com/portainer/portainer/api/datastore/migrator"
"github.com/portainer/portainer/api/datastore/postinit"
"github.com/portainer/portainer/api/docker"
dockerclient "github.com/portainer/portainer/api/docker/client"
"github.com/portainer/portainer/api/exec"
"github.com/portainer/portainer/api/filesystem"
"github.com/portainer/portainer/api/git"
"github.com/portainer/portainer/api/hostmanagement/openamt"
"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/authorization"
"github.com/portainer/portainer/api/internal/edge"
"github.com/portainer/portainer/api/internal/edge/edgestacks"
"github.com/portainer/portainer/api/internal/endpointutils"
"github.com/portainer/portainer/api/internal/snapshot"
"github.com/portainer/portainer/api/internal/ssl"
"github.com/portainer/portainer/api/internal/upgrade"
"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/oauth"
"github.com/portainer/portainer/api/pendingactions"
"github.com/portainer/portainer/api/pendingactions/actions"
"github.com/portainer/portainer/api/pendingactions/handlers"
"github.com/portainer/portainer/api/platform"
"github.com/portainer/portainer/api/scheduler"
"github.com/portainer/portainer/api/stacks"
"github.com/portainer/portainer/api/stacks/deployments"
"github.com/portainer/portainer/pkg/featureflags"
"github.com/portainer/portainer/pkg/libhelm"
"github.com/portainer/portainer/pkg/libstack"
"github.com/portainer/portainer/pkg/libstack/compose"
"github.com/gofrs/uuid"
"github.com/rs/zerolog/log"
)
func initCLI() *portainer.CLIFlags {
var cliService portainer.CLIService = &cli.Service{}
cliService := &cli.Service{}
flags, err := cliService.ParseFlags(portainer.APIVersion)
if err != nil {
logrus.Fatalf("Failed parsing flags: %v", err)
log.Fatal().Err(err).Msg("failed parsing flags")
}
err = cliService.ValidateFlags(flags)
if err != nil {
logrus.Fatalf("Failed validating flags:%v", err)
if err := cliService.ValidateFlags(flags); err != nil {
log.Fatal().Err(err).Msg("failed validating flags")
}
return flags
}
func initFileService(dataStorePath string) portainer.FileService {
fileService, err := filesystem.NewService(dataStorePath, "")
if err != nil {
logrus.Fatalf("Failed creating file service: %v", err)
log.Fatal().Err(err).Msg("failed creating file service")
}
return fileService
}
func initDataStore(flags *portainer.CLIFlags, secretKey []byte, fileService portainer.FileService, shutdownCtx context.Context) dataservices.DataStore {
connection, err := database.NewDatabase("boltdb", *flags.Data, secretKey)
if err != nil {
logrus.Fatalf("failed creating database connection: %s", err)
log.Fatal().Err(err).Msg("failed creating database connection")
}
if bconn, ok := connection.(*boltdb.DbConnection); ok {
@@ -78,80 +92,85 @@ func initDataStore(flags *portainer.CLIFlags, secretKey []byte, fileService port
bconn.MaxBatchDelay = *flags.MaxBatchDelay
bconn.InitialMmapSize = *flags.InitialMmapSize
} else {
logrus.Fatalf("failed creating database connection: expecting a boltdb database type but a different one was received")
log.Fatal().Msg("failed creating database connection: expecting a boltdb database type but a different one was received")
}
store := datastore.NewStore(*flags.Data, fileService, connection)
isNew, err := store.Open()
if err != nil {
logrus.Fatalf("Failed opening store: %v", err)
log.Fatal().Err(err).Msg("failed opening store")
}
if *flags.Rollback {
err := store.Rollback(false)
if err != nil {
logrus.Fatalf("Failed rolling back: %v", err)
if err := store.Rollback(false); err != nil {
log.Fatal().Err(err).Msg("failed rolling back")
}
logrus.Println("Exiting rollback")
log.Info().Msg("exiting rollback")
os.Exit(0)
return nil
}
// Init sets some defaults - it's basically a migration
err = store.Init()
if err != nil {
logrus.Fatalf("Failed initializing data store: %v", err)
if err := store.Init(); err != nil {
log.Fatal().Err(err).Msg("failed initializing data store")
}
if isNew {
// from MigrateData
store.VersionService.StoreDBVersion(portainer.DBVersion)
err := updateSettingsFromFlags(store, flags)
instanceId, err := uuid.NewV4()
if err != nil {
logrus.Fatalf("Failed updating settings from flags: %v", err)
log.Fatal().Err(err).Msg("failed generating instance id")
}
migratorInstance := migrator.NewMigrator(&migrator.MigratorParameters{})
migratorCount := migratorInstance.GetMigratorCountOfCurrentAPIVersion()
// from MigrateData
v := models.Version{
SchemaVersion: portainer.APIVersion,
Edition: int(portainer.PortainerCE),
InstanceID: instanceId.String(),
MigratorCount: migratorCount,
}
store.VersionService.UpdateVersion(&v)
if err := updateSettingsFromFlags(store, flags); err != nil {
log.Fatal().Err(err).Msg("failed updating settings from flags")
}
} else {
storedVersion, err := store.VersionService.DBVersion()
if err != nil {
logrus.Fatalf("Something Failed during creation of new database: %v", err)
}
if storedVersion != portainer.DBVersion {
err = store.MigrateData()
if err != nil {
logrus.Fatalf("Failed migration: %v", err)
}
if err := store.MigrateData(); err != nil {
log.Fatal().Err(err).Msg("failed migration")
}
}
err = updateSettingsFromFlags(store, flags)
if err != nil {
log.Fatalf("Failed updating settings from flags: %v", err)
if err := updateSettingsFromFlags(store, flags); err != nil {
log.Fatal().Err(err).Msg("failed updating settings from flags")
}
// this is for the db restore functionality - needs more tests.
go func() {
<-shutdownCtx.Done()
defer connection.Close()
exportFilename := path.Join(*flags.Data, fmt.Sprintf("export-%d.json", time.Now().Unix()))
err := store.Export(exportFilename)
if err != nil {
logrus.WithError(err).Debugf("Failed to export to %s", exportFilename)
} else {
logrus.Debugf("exported to %s", exportFilename)
}
connection.Close()
}()
return store
}
func initComposeStackManager(assetsPath string, configPath string, reverseTunnelService portainer.ReverseTunnelService, proxyManager *proxy.Manager) portainer.ComposeStackManager {
composeWrapper, err := exec.NewComposeStackManager(assetsPath, configPath, proxyManager)
// checkDBSchemaServerVersionMatch checks if the server version matches the db scehma version
func checkDBSchemaServerVersionMatch(dbStore dataservices.DataStore, serverVersion string, serverEdition int) bool {
v, err := dbStore.Version().Version()
if err != nil {
logrus.Fatalf("Failed creating compose manager: %v", err)
return false
}
return v.SchemaVersion == serverVersion && v.Edition == serverEdition
}
func initComposeStackManager(composeDeployer libstack.Deployer, proxyManager *proxy.Manager) portainer.ComposeStackManager {
composeWrapper, err := exec.NewComposeStackManager(composeDeployer, proxyManager)
if err != nil {
log.Fatal().Err(err).Msg("failed creating compose manager")
}
return composeWrapper
@@ -180,36 +199,21 @@ func initAPIKeyService(datastore dataservices.DataStore) apikey.APIKeyService {
return apikey.NewAPIKeyService(datastore.APIKeyRepository(), datastore.User())
}
func initJWTService(userSessionTimeout string, dataStore dataservices.DataStore) (dataservices.JWTService, error) {
jwtService, err := jwt.NewService(userSessionTimeout, dataStore)
if err != nil {
return nil, err
func initJWTService(userSessionTimeout string, dataStore dataservices.DataStore) (portainer.JWTService, error) {
if userSessionTimeout == "" {
userSessionTimeout = portainer.DefaultUserSessionTimeout
}
return jwtService, nil
return jwt.NewService(userSessionTimeout, dataStore)
}
func initDigitalSignatureService() portainer.DigitalSignatureService {
return crypto.NewECDSAService(os.Getenv("AGENT_SECRET"))
}
func initCryptoService() portainer.CryptoService {
return &crypto.Service{}
}
func initLDAPService() portainer.LDAPService {
return &ldap.Service{}
}
func initOAuthService() portainer.OAuthService {
return oauth.NewService()
}
func initGitService() portainer.GitService {
return git.NewService()
}
func initSSLService(addr, dataPath, certPath, keyPath string, fileService portainer.FileService, dataStore dataservices.DataStore, shutdownTrigger context.CancelFunc) (*ssl.Service, error) {
func initSSLService(addr, certPath, keyPath string, fileService portainer.FileService, dataStore dataservices.DataStore, shutdownTrigger context.CancelFunc) (*ssl.Service, error) {
slices := strings.Split(addr, ":")
host := slices[0]
if host == "" {
host = "0.0.0.0"
@@ -217,27 +221,25 @@ func initSSLService(addr, dataPath, certPath, keyPath string, fileService portai
sslService := ssl.NewService(fileService, dataStore, shutdownTrigger)
err := sslService.Init(host, certPath, keyPath)
if err != nil {
if err := sslService.Init(host, certPath, keyPath); err != nil {
return nil, err
}
return sslService, nil
}
func initDockerClientFactory(signatureService portainer.DigitalSignatureService, reverseTunnelService portainer.ReverseTunnelService) *docker.ClientFactory {
return docker.NewClientFactory(signatureService, reverseTunnelService)
}
func initKubernetesClientFactory(signatureService portainer.DigitalSignatureService, reverseTunnelService portainer.ReverseTunnelService, instanceID string, dataStore dataservices.DataStore) *kubecli.ClientFactory {
return kubecli.NewClientFactory(signatureService, reverseTunnelService, instanceID, dataStore)
}
func initSnapshotService(snapshotIntervalFromFlag string, dataStore dataservices.DataStore, dockerClientFactory *docker.ClientFactory, kubernetesClientFactory *kubecli.ClientFactory, shutdownCtx context.Context) (portainer.SnapshotService, error) {
func initSnapshotService(
snapshotIntervalFromFlag string,
dataStore dataservices.DataStore,
dockerClientFactory *dockerclient.ClientFactory,
kubernetesClientFactory *kubecli.ClientFactory,
shutdownCtx context.Context,
pendingActionsService *pendingactions.PendingActionsService,
) (portainer.SnapshotService, error) {
dockerSnapshotter := docker.NewSnapshotter(dockerClientFactory)
kubernetesSnapshotter := kubernetes.NewSnapshotter(kubernetesClientFactory)
snapshotService, err := snapshot.NewService(snapshotIntervalFromFlag, dataStore, dockerSnapshotter, kubernetesSnapshotter, shutdownCtx)
snapshotService, err := snapshot.NewService(snapshotIntervalFromFlag, dataStore, dockerSnapshotter, kubernetesSnapshotter, shutdownCtx, pendingActionsService)
if err != nil {
return nil, err
}
@@ -258,28 +260,21 @@ func updateSettingsFromFlags(dataStore dataservices.DataStore, flags *portainer.
return err
}
if *flags.SnapshotInterval != "" {
settings.SnapshotInterval = *flags.SnapshotInterval
}
if *flags.Logo != "" {
settings.LogoURL = *flags.Logo
}
if *flags.EnableEdgeComputeFeatures {
settings.EnableEdgeComputeFeatures = *flags.EnableEdgeComputeFeatures
}
if *flags.Templates != "" {
settings.TemplatesURL = *flags.Templates
}
settings.SnapshotInterval = *cmp.Or(flags.SnapshotInterval, &settings.SnapshotInterval)
settings.LogoURL = *cmp.Or(flags.Logo, &settings.LogoURL)
settings.EnableEdgeComputeFeatures = *cmp.Or(flags.EnableEdgeComputeFeatures, &settings.EnableEdgeComputeFeatures)
settings.TemplatesURL = *cmp.Or(flags.Templates, &settings.TemplatesURL)
if *flags.Labels != nil {
settings.BlackListedLabels = *flags.Labels
}
err = dataStore.Settings().UpdateSettings(settings)
if err != nil {
settings.AgentSecret = ""
if agentKey, ok := os.LookupEnv("AGENT_SECRET"); ok {
settings.AgentSecret = agentKey
}
if err := dataStore.Settings().UpdateSettings(settings); err != nil {
return err
}
@@ -294,55 +289,7 @@ func updateSettingsFromFlags(dataStore dataservices.DataStore, flags *portainer.
sslSettings.HTTPEnabled = true
}
err = dataStore.SSLSettings().UpdateSettings(sslSettings)
if err != nil {
return err
}
return nil
}
// enableFeaturesFromFlags turns on or off feature flags
// e.g. portainer --feat open-amt --feat fdo=true ... (defaults to true)
// note, settings are persisted to the DB. To turn off `--feat open-amt=false`
func enableFeaturesFromFlags(dataStore dataservices.DataStore, flags *portainer.CLIFlags) error {
settings, err := dataStore.Settings().Settings()
if err != nil {
return err
}
if settings.FeatureFlagSettings == nil {
settings.FeatureFlagSettings = make(map[portainer.Feature]bool)
}
// loop through feature flags to check if they are supported
for _, feat := range *flags.FeatureFlags {
var correspondingFeature *portainer.Feature
for i, supportedFeat := range portainer.SupportedFeatureFlags {
if strings.EqualFold(feat.Name, string(supportedFeat)) {
correspondingFeature = &portainer.SupportedFeatureFlags[i]
}
}
if correspondingFeature == nil {
return fmt.Errorf("unknown feature flag '%s'", feat.Name)
}
featureState, err := strconv.ParseBool(feat.Value)
if err != nil {
return fmt.Errorf("feature flag's '%s' value should be true or false", feat.Name)
}
if featureState {
logrus.Printf("Feature %v : on", *correspondingFeature)
} else {
logrus.Printf("Feature %v : off", *correspondingFeature)
}
settings.FeatureFlagSettings[*correspondingFeature] = featureState
}
return dataStore.Settings().UpdateSettings(settings)
return dataStore.SSLSettings().UpdateSettings(sslSettings)
}
func loadAndParseKeyPair(fileService portainer.FileService, signatureService portainer.DigitalSignatureService) error {
@@ -350,6 +297,7 @@ func loadAndParseKeyPair(fileService portainer.FileService, signatureService por
if err != nil {
return err
}
return signatureService.ParseKeyPair(private, public)
}
@@ -358,162 +306,32 @@ func generateAndStoreKeyPair(fileService portainer.FileService, signatureService
if err != nil {
return err
}
privateHeader, publicHeader := signatureService.PEMHeaders()
return fileService.StoreKeyPair(private, public, privateHeader, publicHeader)
}
func initKeyPair(fileService portainer.FileService, signatureService portainer.DigitalSignatureService) error {
existingKeyPair, err := fileService.KeyPairFilesExist()
if err != nil {
logrus.Fatalf("Failed checking for existing key pair: %v", err)
log.Fatal().Err(err).Msg("failed checking for existing key pair")
}
if existingKeyPair {
return loadAndParseKeyPair(fileService, signatureService)
}
return generateAndStoreKeyPair(fileService, signatureService)
}
func createTLSSecuredEndpoint(flags *portainer.CLIFlags, dataStore dataservices.DataStore, snapshotService portainer.SnapshotService) error {
tlsConfiguration := portainer.TLSConfiguration{
TLS: *flags.TLS,
TLSSkipVerify: *flags.TLSSkipVerify,
}
if *flags.TLS {
tlsConfiguration.TLSCACertPath = *flags.TLSCacert
tlsConfiguration.TLSCertPath = *flags.TLSCert
tlsConfiguration.TLSKeyPath = *flags.TLSKey
} else if !*flags.TLS && *flags.TLSSkipVerify {
tlsConfiguration.TLS = true
}
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,
UserAccessPolicies: portainer.UserAccessPolicies{},
TeamAccessPolicies: portainer.TeamAccessPolicies{},
TagIDs: []portainer.TagID{},
Status: portainer.EndpointStatusUp,
Snapshots: []portainer.DockerSnapshot{},
Kubernetes: portainer.KubernetesDefault(),
SecuritySettings: portainer.EndpointSecuritySettings{
AllowVolumeBrowserForRegularUsers: false,
EnableHostManagementFeatures: false,
AllowSysctlSettingForRegularUsers: true,
AllowBindMountsForRegularUsers: true,
AllowPrivilegedModeForRegularUsers: true,
AllowHostNamespaceForRegularUsers: true,
AllowContainerCapabilitiesForRegularUsers: true,
AllowDeviceMappingForRegularUsers: true,
AllowStackManagementForRegularUsers: true,
},
}
if strings.HasPrefix(endpoint.URL, "tcp://") {
tlsConfig, err := crypto.CreateTLSConfigurationFromDisk(tlsConfiguration.TLSCACertPath, tlsConfiguration.TLSCertPath, tlsConfiguration.TLSKeyPath, tlsConfiguration.TLSSkipVerify)
if err != nil {
return err
}
agentOnDockerEnvironment, err := client.ExecutePingOperation(endpoint.URL, tlsConfig)
if err != nil {
return err
}
if agentOnDockerEnvironment {
endpoint.Type = portainer.AgentOnDockerEnvironment
}
}
err := snapshotService.SnapshotEndpoint(endpoint)
if err != nil {
logrus.Printf("http error: environment snapshot error (environment=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
}
return dataStore.Endpoint().Create(endpoint)
}
func createUnsecuredEndpoint(endpointURL string, dataStore dataservices.DataStore, snapshotService portainer.SnapshotService) error {
if strings.HasPrefix(endpointURL, "tcp://") {
_, err := client.ExecutePingOperation(endpointURL, nil)
if err != nil {
return err
}
}
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{},
UserAccessPolicies: portainer.UserAccessPolicies{},
TeamAccessPolicies: portainer.TeamAccessPolicies{},
TagIDs: []portainer.TagID{},
Status: portainer.EndpointStatusUp,
Snapshots: []portainer.DockerSnapshot{},
Kubernetes: portainer.KubernetesDefault(),
SecuritySettings: portainer.EndpointSecuritySettings{
AllowVolumeBrowserForRegularUsers: false,
EnableHostManagementFeatures: false,
AllowSysctlSettingForRegularUsers: true,
AllowBindMountsForRegularUsers: true,
AllowPrivilegedModeForRegularUsers: true,
AllowHostNamespaceForRegularUsers: true,
AllowContainerCapabilitiesForRegularUsers: true,
AllowDeviceMappingForRegularUsers: true,
AllowStackManagementForRegularUsers: true,
},
}
err := snapshotService.SnapshotEndpoint(endpoint)
if err != nil {
logrus.Printf("http error: environment snapshot error (environment=%s, URL=%s) (err=%s)\n", endpoint.Name, endpoint.URL, err)
}
return dataStore.Endpoint().Create(endpoint)
}
func initEndpoint(flags *portainer.CLIFlags, dataStore dataservices.DataStore, snapshotService portainer.SnapshotService) error {
if *flags.EndpointURL == "" {
return nil
}
endpoints, err := dataStore.Endpoint().Endpoints()
if err != nil {
return err
}
if len(endpoints) > 0 {
logrus.Println("Instance already has defined environments. Skipping the environment defined via CLI.")
return nil
}
if *flags.TLS || *flags.TLSSkipVerify {
return createTLSSecuredEndpoint(flags, dataStore, snapshotService)
}
return createUnsecuredEndpoint(*flags.EndpointURL, dataStore, snapshotService)
}
func loadEncryptionSecretKey(keyfilename string) []byte {
content, err := os.ReadFile(path.Join("/run/secrets", keyfilename))
if err != nil {
if os.IsNotExist(err) {
logrus.Printf("Encryption key file `%s` not present", keyfilename)
log.Info().Str("filename", keyfilename).Msg("encryption key file not present")
} else {
logrus.Printf("Error reading encryption key file: %v", err)
log.Info().Err(err).Msg("error reading encryption key file")
}
return nil
@@ -521,79 +339,88 @@ func loadEncryptionSecretKey(keyfilename string) []byte {
// return a 32 byte hash of the secret (required for AES)
hash := sha256.Sum256(content)
return hash[:]
}
func buildServer(flags *portainer.CLIFlags) portainer.Server {
shutdownCtx, shutdownTrigger := context.WithCancel(context.Background())
if flags.FeatureFlags != nil {
featureflags.Parse(*flags.FeatureFlags, portainer.SupportedFeatureFlags)
}
fileService := initFileService(*flags.Data)
encryptionKey := loadEncryptionSecretKey(*flags.SecretKeyName)
if encryptionKey == nil {
logrus.Println("Proceeding without encryption key")
log.Info().Msg("proceeding without encryption key")
}
dataStore := initDataStore(flags, encryptionKey, fileService, shutdownCtx)
if err := dataStore.CheckCurrentEdition(); err != nil {
logrus.Fatal(err)
log.Fatal().Err(err).Msg("")
}
// check if the db schema version matches with server version
if !checkDBSchemaServerVersionMatch(dataStore, portainer.APIVersion, int(portainer.Edition)) {
log.Fatal().Msg("The database schema version does not align with the server version. Please consider reverting to the previous server version or addressing the database migration issue.")
}
instanceID, err := dataStore.Version().InstanceID()
if err != nil {
logrus.Fatalf("Failed getting instance id: %v", err)
log.Fatal().Err(err).Msg("failed getting instance id")
}
apiKeyService := initAPIKeyService(dataStore)
settings, err := dataStore.Settings().Settings()
if err != nil {
logrus.Fatal(err)
log.Fatal().Err(err).Msg("")
}
jwtService, err := initJWTService(settings.UserSessionTimeout, dataStore)
if err != nil {
logrus.Fatalf("Failed initializing JWT service: %v", err)
log.Fatal().Err(err).Msg("failed initializing JWT service")
}
err = enableFeaturesFromFlags(dataStore, flags)
if err != nil {
logrus.Fatalf("Failed enabling feature flag: %v", err)
}
ldapService := &ldap.Service{}
ldapService := initLDAPService()
oauthService := initOAuthService()
gitService := initGitService()
oauthService := oauth.NewService()
gitService := git.NewService(shutdownCtx)
openAMTService := openamt.NewService()
cryptoService := initCryptoService()
digitalSignatureService := initDigitalSignatureService()
cryptoService := &crypto.Service{}
sslService, err := initSSLService(*flags.AddrHTTPS, *flags.Data, *flags.SSLCert, *flags.SSLKey, fileService, dataStore, shutdownTrigger)
signatureService := initDigitalSignatureService()
edgeStacksService := edgestacks.NewService(dataStore)
sslService, err := initSSLService(*flags.AddrHTTPS, *flags.SSLCert, *flags.SSLKey, fileService, dataStore, shutdownTrigger)
if err != nil {
logrus.Fatal(err)
log.Fatal().Err(err).Msg("")
}
sslSettings, err := sslService.GetSSLSettings()
if err != nil {
logrus.Fatalf("Failed to get ssl settings: %s", err)
log.Fatal().Err(err).Msg("failed to get SSL settings")
}
err = initKeyPair(fileService, digitalSignatureService)
if err := initKeyPair(fileService, signatureService); err != nil {
log.Fatal().Err(err).Msg("failed initializing key pair")
}
reverseTunnelService := chisel.NewService(dataStore, shutdownCtx, fileService)
dockerClientFactory := dockerclient.NewClientFactory(signatureService, reverseTunnelService)
kubernetesClientFactory, err := kubecli.NewClientFactory(signatureService, reverseTunnelService, dataStore, instanceID, *flags.AddrHTTPS, settings.UserSessionTimeout)
if err != nil {
logrus.Fatalf("Failed initializing key pair: %v", err)
log.Fatal().Err(err).Msg("failed initializing Kubernetes Client Factory service")
}
reverseTunnelService := chisel.NewService(dataStore, shutdownCtx)
dockerClientFactory := initDockerClientFactory(digitalSignatureService, reverseTunnelService)
kubernetesClientFactory := initKubernetesClientFactory(digitalSignatureService, reverseTunnelService, instanceID, dataStore)
snapshotService, err := initSnapshotService(*flags.SnapshotInterval, dataStore, dockerClientFactory, kubernetesClientFactory, shutdownCtx)
if err != nil {
logrus.Fatalf("Failed initializing snapshot service: %v", err)
}
snapshotService.Start()
authorizationService := authorization.NewService(dataStore)
authorizationService.K8sClientFactory = kubernetesClientFactory
@@ -601,47 +428,67 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService(*flags.BaseURL, *flags.AddrHTTPS, sslSettings.CertPath)
proxyManager := proxy.NewManager(dataStore, digitalSignatureService, reverseTunnelService, dockerClientFactory, kubernetesClientFactory, kubernetesTokenCacheManager)
proxyManager := proxy.NewManager(kubernetesClientFactory)
reverseTunnelService.ProxyManager = proxyManager
dockerConfigPath := fileService.GetDockerConfigPath()
composeStackManager := initComposeStackManager(*flags.Assets, dockerConfigPath, reverseTunnelService, proxyManager)
swarmStackManager, err := initSwarmStackManager(*flags.Assets, dockerConfigPath, digitalSignatureService, fileService, reverseTunnelService, dataStore)
composeDeployer, err := compose.NewComposeDeployer(*flags.Assets, dockerConfigPath)
if err != nil {
logrus.Fatalf("Failed initializing swarm stack manager: %v", err)
log.Fatal().Err(err).Msg("failed initializing compose deployer")
}
kubernetesDeployer := initKubernetesDeployer(kubernetesTokenCacheManager, kubernetesClientFactory, dataStore, reverseTunnelService, digitalSignatureService, proxyManager, *flags.Assets)
composeStackManager := initComposeStackManager(composeDeployer, proxyManager)
swarmStackManager, err := initSwarmStackManager(*flags.Assets, dockerConfigPath, signatureService, fileService, reverseTunnelService, dataStore)
if err != nil {
log.Fatal().Err(err).Msg("failed initializing swarm stack manager")
}
kubernetesDeployer := initKubernetesDeployer(kubernetesTokenCacheManager, kubernetesClientFactory, dataStore, reverseTunnelService, signatureService, proxyManager, *flags.Assets)
pendingActionsService := pendingactions.NewService(dataStore, kubernetesClientFactory)
pendingActionsService.RegisterHandler(actions.CleanNAPWithOverridePolicies, handlers.NewHandlerCleanNAPWithOverridePolicies(authorizationService, dataStore))
pendingActionsService.RegisterHandler(actions.DeletePortainerK8sRegistrySecrets, handlers.NewHandlerDeleteRegistrySecrets(authorizationService, dataStore, kubernetesClientFactory))
pendingActionsService.RegisterHandler(actions.PostInitMigrateEnvironment, handlers.NewHandlerPostInitMigrateEnvironment(authorizationService, dataStore, kubernetesClientFactory, dockerClientFactory, *flags.Assets, kubernetesDeployer))
snapshotService, err := initSnapshotService(*flags.SnapshotInterval, dataStore, dockerClientFactory, kubernetesClientFactory, shutdownCtx, pendingActionsService)
if err != nil {
log.Fatal().Err(err).Msg("failed initializing snapshot service")
}
snapshotService.Start()
proxyManager.NewProxyFactory(dataStore, signatureService, reverseTunnelService, dockerClientFactory, kubernetesClientFactory, kubernetesTokenCacheManager, gitService, snapshotService)
helmPackageManager, err := initHelmPackageManager(*flags.Assets)
if err != nil {
logrus.Fatalf("Failed initializing helm package manager: %v", err)
log.Fatal().Err(err).Msg("failed initializing helm package manager")
}
err = edge.LoadEdgeJobs(dataStore, reverseTunnelService)
if err != nil {
logrus.Fatalf("Failed loading edge jobs from database: %v", err)
if err := edge.LoadEdgeJobs(dataStore, reverseTunnelService); err != nil {
log.Fatal().Err(err).Msg("failed loading edge jobs from database")
}
applicationStatus := initStatus(instanceID)
err = initEndpoint(flags, dataStore, snapshotService)
if err != nil {
logrus.Fatalf("Failed initializing environment: %v", err)
}
// channel to control when the admin user is created
adminCreationDone := make(chan struct{}, 1)
go endpointutils.InitEndpoint(shutdownCtx, adminCreationDone, flags, dataStore, snapshotService)
adminPasswordHash := ""
if *flags.AdminPasswordFile != "" {
content, err := fileService.GetFileContent(*flags.AdminPasswordFile, "")
if err != nil {
logrus.Fatalf("Failed getting admin password file: %v", err)
log.Fatal().Err(err).Msg("failed getting admin password file")
}
adminPasswordHash, err = cryptoService.Hash(strings.TrimSuffix(string(content), "\n"))
if err != nil {
logrus.Fatalf("Failed hashing admin password: %v", err)
log.Fatal().Err(err).Msg("failed hashing admin password")
}
} else if *flags.AdminPassword != "" {
adminPasswordHash = *flags.AdminPassword
@@ -650,38 +497,74 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
if adminPasswordHash != "" {
users, err := dataStore.User().UsersByRole(portainer.AdministratorRole)
if err != nil {
logrus.Fatalf("Failed getting admin user: %v", err)
log.Fatal().Err(err).Msg("failed getting admin user")
}
if len(users) == 0 {
logrus.Println("Created admin user with the given password.")
log.Info().Msg("created admin user with the given password.")
user := &portainer.User{
Username: "admin",
Role: portainer.AdministratorRole,
Password: adminPasswordHash,
}
err := dataStore.User().Create(user)
if err != nil {
logrus.Fatalf("Failed creating admin user: %v", err)
if err := dataStore.User().Create(user); err != nil {
log.Fatal().Err(err).Msg("failed creating admin user")
}
// notify the admin user is created, the endpoint initialization can start
adminCreationDone <- struct{}{}
} else {
logrus.Println("Instance already has an administrator user defined. Skipping admin password related flags.")
log.Info().Msg("instance already has an administrator user defined, skipping admin password related flags.")
}
}
err = reverseTunnelService.StartTunnelServer(*flags.TunnelAddr, *flags.TunnelPort, snapshotService)
if err != nil {
logrus.Fatalf("Failed starting tunnel server: %v", err)
}
sslDBSettings, err := dataStore.SSLSettings().Settings()
if err != nil {
logrus.Fatalf("Failed to fetch ssl settings from DB")
if err := reverseTunnelService.StartTunnelServer(*flags.TunnelAddr, *flags.TunnelPort, snapshotService); err != nil {
log.Fatal().Err(err).Msg("failed starting tunnel server")
}
scheduler := scheduler.NewScheduler(shutdownCtx)
stackDeployer := stacks.NewStackDeployer(swarmStackManager, composeStackManager, kubernetesDeployer)
stacks.StartStackSchedules(scheduler, stackDeployer, dataStore, gitService)
stackDeployer := deployments.NewStackDeployer(swarmStackManager, composeStackManager, kubernetesDeployer, dockerClientFactory, dataStore)
deployments.StartStackSchedules(scheduler, stackDeployer, dataStore, gitService)
sslDBSettings, err := dataStore.SSLSettings().Settings()
if err != nil {
log.Fatal().Msg("failed to fetch SSL settings from DB")
}
platformService, err := platform.NewService(dataStore)
if err != nil {
log.Fatal().Err(err).Msg("failed initializing platform service")
}
upgradeService, err := upgrade.NewService(
*flags.Assets,
kubernetesClientFactory,
dockerClientFactory,
composeStackManager,
dataStore,
fileService,
stackDeployer,
)
if err != nil {
log.Fatal().Err(err).Msg("failed initializing upgrade service")
}
// Our normal migrations run as part of the database initialization
// but some more complex migrations require access to a kubernetes or docker
// client. Therefore we run a separate migration process just before
// starting the server.
postInitMigrator := postinit.NewPostInitMigrator(
kubernetesClientFactory,
dockerClientFactory,
dataStore,
*flags.Assets,
kubernetesDeployer,
)
if err := postInitMigrator.PostInitMigrate(); err != nil {
log.Fatal().Err(err).Msg("failure during post init migrations")
}
return &http.Server{
AuthorizationService: authorizationService,
@@ -692,12 +575,13 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
HTTPEnabled: sslDBSettings.HTTPEnabled,
AssetsPath: *flags.Assets,
DataStore: dataStore,
EdgeStacksService: edgeStacksService,
SwarmStackManager: swarmStackManager,
ComposeStackManager: composeStackManager,
KubernetesDeployer: kubernetesDeployer,
HelmPackageManager: helmPackageManager,
CryptoService: cryptoService,
APIKeyService: apiKeyService,
CryptoService: cryptoService,
JWTService: jwtService,
FileService: fileService,
LDAPService: ldapService,
@@ -707,7 +591,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
ProxyManager: proxyManager,
KubernetesTokenCacheManager: kubernetesTokenCacheManager,
KubeClusterAccessService: kubeClusterAccessService,
SignatureService: digitalSignatureService,
SignatureService: signatureService,
SnapshotService: snapshotService,
SSLService: sslService,
DockerClientFactory: dockerClientFactory,
@@ -716,18 +600,37 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
ShutdownCtx: shutdownCtx,
ShutdownTrigger: shutdownTrigger,
StackDeployer: stackDeployer,
UpgradeService: upgradeService,
AdminCreationDone: adminCreationDone,
PendingActionsService: pendingActionsService,
PlatformService: platformService,
}
}
func main() {
configureLogger()
setLoggingMode("PRETTY")
flags := initCLI()
configureLogger()
setLoggingLevel(*flags.LogLevel)
setLoggingMode(*flags.LogMode)
for {
server := buildServer(flags)
logrus.Printf("[INFO] [cmd,main] Starting Portainer version %s\n", portainer.APIVersion)
log.Info().
Str("version", portainer.APIVersion).
Str("build_number", build.BuildNumber).
Str("image_tag", build.ImageTag).
Str("nodejs_version", build.NodejsVersion).
Str("yarn_version", build.YarnVersion).
Str("webpack_version", build.WebpackVersion).
Str("go_version", build.GoVersion).
Msg("starting Portainer")
err := server.Start()
logrus.Printf("[INFO] [cmd,main] Http server exited: %v\n", err)
log.Info().Err(err).Msg("HTTP server exited")
}
}

View File

@@ -1,110 +0,0 @@
package main
import (
"fmt"
"testing"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/cli"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/datastore"
"github.com/stretchr/testify/assert"
"gopkg.in/alecthomas/kingpin.v2"
)
type mockKingpinSetting string
func (m mockKingpinSetting) SetValue(value kingpin.Value) {
value.Set(string(m))
}
func Test_enableFeaturesFromFlags(t *testing.T) {
is := assert.New(t)
_, store, teardown := datastore.MustNewTestStore(true)
defer teardown()
tests := []struct {
featureFlag string
isSupported bool
}{
{"test", false},
}
for _, test := range tests {
t.Run(fmt.Sprintf("%s succeeds:%v", test.featureFlag, test.isSupported), func(t *testing.T) {
mockKingpinSetting := mockKingpinSetting(test.featureFlag)
flags := &portainer.CLIFlags{FeatureFlags: cli.BoolPairs(mockKingpinSetting)}
err := enableFeaturesFromFlags(store, flags)
if test.isSupported {
is.NoError(err)
} else {
is.Error(err)
}
})
}
t.Run("passes for all supported feature flags", func(t *testing.T) {
for _, flag := range portainer.SupportedFeatureFlags {
mockKingpinSetting := mockKingpinSetting(flag)
flags := &portainer.CLIFlags{FeatureFlags: cli.BoolPairs(mockKingpinSetting)}
err := enableFeaturesFromFlags(store, flags)
is.NoError(err)
}
})
}
const FeatTest portainer.Feature = "optional-test"
func optionalFunc(dataStore dataservices.DataStore) string {
// TODO: this is a code smell - finding out if a feature flag is enabled should not require having access to the store, and asking for a settings obj.
// ideally, the `if` should look more like:
// if featureflags.FlagEnabled(FeatTest) {}
settings, err := dataStore.Settings().Settings()
if err != nil {
return err.Error()
}
if settings.FeatureFlagSettings[FeatTest] {
return "enabled"
}
return "disabled"
}
func Test_optionalFeature(t *testing.T) {
portainer.SupportedFeatureFlags = append(portainer.SupportedFeatureFlags, FeatTest)
is := assert.New(t)
_, store, teardown := datastore.MustNewTestStore(true)
defer teardown()
// Enable the test feature
t.Run(fmt.Sprintf("%s succeeds:%v", FeatTest, true), func(t *testing.T) {
mockKingpinSetting := mockKingpinSetting(FeatTest)
flags := &portainer.CLIFlags{FeatureFlags: cli.BoolPairs(mockKingpinSetting)}
err := enableFeaturesFromFlags(store, flags)
is.NoError(err)
is.Equal("enabled", optionalFunc(store))
})
// Same store, so the feature flag should still be enabled
t.Run(fmt.Sprintf("%s succeeds:%v", FeatTest, true), func(t *testing.T) {
is.Equal("enabled", optionalFunc(store))
})
// disable the test feature
t.Run(fmt.Sprintf("%s succeeds:%v", FeatTest, true), func(t *testing.T) {
mockKingpinSetting := mockKingpinSetting(FeatTest + "=false")
flags := &portainer.CLIFlags{FeatureFlags: cli.BoolPairs(mockKingpinSetting)}
err := enableFeaturesFromFlags(store, flags)
is.NoError(err)
is.Equal("disabled", optionalFunc(store))
})
// Same store, so feature flag should still be disabled
t.Run(fmt.Sprintf("%s succeeds:%v", FeatTest, true), func(t *testing.T) {
is.Equal("disabled", optionalFunc(store))
})
}

View File

@@ -0,0 +1,148 @@
// Package concurrent provides utilities for running multiple functions concurrently in Go.
// For example, many kubernetes calls can take a while to fulfill. Oftentimes in Portainer
// we need to get a list of objects from multiple kubernetes REST APIs. We can often call these
// apis concurrently to speed up the response time.
// This package provides a clean way to do just that.
//
// Examples:
// The ConfigMaps and Secrets function converted using concurrent.Run.
/*
// GetConfigMapsAndSecrets gets all the ConfigMaps AND all the Secrets for a
// given namespace in a k8s endpoint. The result is a list of both config maps
// and secrets. The IsSecret boolean property indicates if a given struct is a
// secret or configmap.
func (kcl *KubeClient) GetConfigMapsAndSecrets(namespace string) ([]models.K8sConfigMapOrSecret, error) {
// use closures to capture the current kube client and namespace by declaring wrapper functions
// that match the interface signature for concurrent.Func
listConfigMaps := func(ctx context.Context) (any, error) {
return kcl.cli.CoreV1().ConfigMaps(namespace).List(context.Background(), meta.ListOptions{})
}
listSecrets := func(ctx context.Context) (any, error) {
return kcl.cli.CoreV1().Secrets(namespace).List(context.Background(), meta.ListOptions{})
}
// run the functions concurrently and wait for results. We can also pass in a context to cancel.
// e.g. Deadline timer.
results, err := concurrent.Run(context.TODO(), listConfigMaps, listSecrets)
if err != nil {
return nil, err
}
var configMapList *core.ConfigMapList
var secretList *core.SecretList
for _, r := range results {
switch v := r.Result.(type) {
case *core.ConfigMapList:
configMapList = v
case *core.SecretList:
secretList = v
}
}
// TODO: Applications
var combined []models.K8sConfigMapOrSecret
for _, m := range configMapList.Items {
var cm models.K8sConfigMapOrSecret
cm.UID = string(m.UID)
cm.Name = m.Name
cm.Namespace = m.Namespace
cm.Annotations = m.Annotations
cm.Data = m.Data
cm.CreationDate = m.CreationTimestamp.Time.UTC().Format(time.RFC3339)
combined = append(combined, cm)
}
for _, s := range secretList.Items {
var secret models.K8sConfigMapOrSecret
secret.UID = string(s.UID)
secret.Name = s.Name
secret.Namespace = s.Namespace
secret.Annotations = s.Annotations
secret.Data = msbToMss(s.Data)
secret.CreationDate = s.CreationTimestamp.Time.UTC().Format(time.RFC3339)
secret.IsSecret = true
secret.SecretType = string(s.Type)
combined = append(combined, secret)
}
return combined, nil
}
*/
package concurrent
import (
"context"
"sync"
)
// Result contains the result and any error returned from running a client task function
type Result struct {
Result any // the result of running the task function
Err error // any error that occurred while running the task function
}
// Func is a function returns a result or error
type Func func(ctx context.Context) (any, error)
// Run runs a list of functions returns the results
func Run(ctx context.Context, maxConcurrency int, tasks ...Func) ([]Result, error) {
var wg sync.WaitGroup
resultsChan := make(chan Result, len(tasks))
taskChan := make(chan Func, len(tasks))
localCtx, cancelCtx := context.WithCancel(ctx)
defer cancelCtx()
runTask := func() {
defer wg.Done()
for fn := range taskChan {
result, err := fn(localCtx)
resultsChan <- Result{Result: result, Err: err}
}
}
// Set maxConcurrency to the number of tasks if zero or negative
if maxConcurrency <= 0 {
maxConcurrency = len(tasks)
}
// Start worker goroutines
for range maxConcurrency {
wg.Add(1)
go runTask()
}
// Add tasks to the task channel
for _, fn := range tasks {
taskChan <- fn
}
// Close the task channel to signal workers to stop when all tasks are done
close(taskChan)
// Wait for all workers to complete
wg.Wait()
close(resultsChan)
// Collect the results and cancel on error
results := make([]Result, 0, len(tasks))
for r := range resultsChan {
if r.Err != nil {
cancelCtx()
return nil, r.Err
}
results = append(results, r)
}
return results, nil
}

View File

@@ -4,10 +4,34 @@ import (
"io"
)
type ReadTransaction interface {
GetObject(bucketName string, key []byte, object any) error
GetAll(bucketName string, obj any, append func(o any) (any, error)) error
GetAllWithKeyPrefix(bucketName string, keyPrefix []byte, obj any, append func(o any) (any, error)) error
}
type Transaction interface {
ReadTransaction
SetServiceName(bucketName string) error
UpdateObject(bucketName string, key []byte, object any) error
DeleteObject(bucketName string, key []byte) error
CreateObject(bucketName string, fn func(uint64) (int, any)) error
CreateObjectWithId(bucketName string, id int, obj any) error
CreateObjectWithStringId(bucketName string, id []byte, obj any) error
DeleteAllObjects(bucketName string, obj any, matching func(o any) (id int, ok bool)) error
GetNextIdentifier(bucketName string) int
}
type Connection interface {
Transaction
Open() error
Close() error
UpdateTx(fn func(Transaction) error) error
ViewTx(fn func(Transaction) error) error
// write the db contents to filename as json (the schema needs defining)
ExportRaw(filename string) error
@@ -21,20 +45,9 @@ type Connection interface {
NeedsEncryptionMigration() (bool, error)
SetEncrypted(encrypted bool)
SetServiceName(bucketName string) error
GetObject(bucketName string, key []byte, object interface{}) error
UpdateObject(bucketName string, key []byte, object interface{}) error
DeleteObject(bucketName string, key []byte) error
DeleteAllObjects(bucketName string, matching func(o interface{}) (id int, ok bool)) error
GetNextIdentifier(bucketName string) int
CreateObject(bucketName string, fn func(uint64) (int, interface{})) error
CreateObjectWithId(bucketName string, id int, obj interface{}) error
CreateObjectWithStringId(bucketName string, id []byte, obj interface{}) error
CreateObjectWithSetSequence(bucketName string, id int, obj interface{}) error
GetAll(bucketName string, obj interface{}, append func(o interface{}) (interface{}, error)) error
GetAllWithJsoniter(bucketName string, obj interface{}, append func(o interface{}) (interface{}, error)) error
ConvertToKey(v int) []byte
BackupMetadata() (map[string]any, error)
RestoreMetadata(s map[string]any) error
BackupMetadata() (map[string]interface{}, error)
RestoreMetadata(s map[string]interface{}) error
UpdateObjectFunc(bucketName string, key []byte, object any, updateFn func()) error
ConvertToKey(v int) []byte
}

View File

@@ -1,52 +1,216 @@
package crypto
import (
"bufio"
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"fmt"
"io"
"golang.org/x/crypto/argon2"
"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
const (
// AES GCM settings
aesGcmHeader = "AES256-GCM" // The encrypted file header
aesGcmBlockSize = 1024 * 1024 // 1MB block for aes gcm
var emptySalt []byte = make([]byte, 0, 0)
// Argon2 settings
// Recommded settings lower memory hardware according to current OWASP recommendations
// Considering some people run portainer on a NAS I think it's prudent not to assume we're on server grade hardware
// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#argon2id
argon2MemoryCost = 12 * 1024
argon2TimeCost = 3
argon2Threads = 1
argon2KeyLength = 32
)
// AesEncrypt reads from input, encrypts with AES-256 and writes to the output.
// passphrase is used to generate an encryption key.
// AesEncrypt reads from input, encrypts with AES-256 and writes to 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)
err := aesEncryptGCM(input, output, passphrase)
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 fmt.Errorf("error encrypting file: %w", 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.
// AesDecrypt reads from input, decrypts with AES-256 and returns the reader to read the decrypted content from
func AesDecrypt(input io.Reader, passphrase []byte) (io.Reader, error) {
// Read file header to determine how it was encrypted
inputReader := bufio.NewReader(input)
header, err := inputReader.Peek(len(aesGcmHeader))
if err != nil {
return nil, fmt.Errorf("error reading encrypted backup file header: %w", err)
}
if string(header) == aesGcmHeader {
reader, err := aesDecryptGCM(inputReader, passphrase)
if err != nil {
return nil, fmt.Errorf("error decrypting file: %w", err)
}
return reader, nil
}
// Use the previous decryption routine which has no header (to support older archives)
reader, err := aesDecryptOFB(inputReader, passphrase)
if err != nil {
return nil, fmt.Errorf("error decrypting legacy file backup: %w", err)
}
return reader, nil
}
// aesEncryptGCM reads from input, encrypts with AES-256 and writes to output. passphrase is used to generate an encryption key.
func aesEncryptGCM(input io.Reader, output io.Writer, passphrase []byte) error {
// Derive key using argon2 with a random salt
salt := make([]byte, 16) // 16 bytes salt
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
return err
}
key := argon2.IDKey(passphrase, salt, argon2TimeCost, argon2MemoryCost, argon2Threads, 32)
block, err := aes.NewCipher(key)
if err != nil {
return err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return err
}
// Generate nonce
nonce, err := NewRandomNonce(aesgcm.NonceSize())
if err != nil {
return err
}
// write the header
if _, err := output.Write([]byte(aesGcmHeader)); err != nil {
return err
}
// Write nonce and salt to the output file
if _, err := output.Write(salt); err != nil {
return err
}
if _, err := output.Write(nonce.Value()); err != nil {
return err
}
// Buffer for reading plaintext blocks
buf := make([]byte, aesGcmBlockSize) // Adjust buffer size as needed
ciphertext := make([]byte, len(buf)+aesgcm.Overhead())
// Encrypt plaintext in blocks
for {
n, err := io.ReadFull(input, buf)
if n == 0 {
break // end of plaintext input
}
if err != nil && !(errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF)) {
return err
}
// Seal encrypts the plaintext using the nonce returning the updated slice.
ciphertext = aesgcm.Seal(ciphertext[:0], nonce.Value(), buf[:n], nil)
_, err = output.Write(ciphertext)
if err != nil {
return err
}
nonce.Increment()
}
return nil
}
// aesDecryptGCM reads from input, decrypts with AES-256 and returns the reader to read the decrypted content from.
func aesDecryptGCM(input io.Reader, passphrase []byte) (io.Reader, error) {
// Reader & verify header
header := make([]byte, len(aesGcmHeader))
if _, err := io.ReadFull(input, header); err != nil {
return nil, err
}
if string(header) != aesGcmHeader {
return nil, fmt.Errorf("invalid header")
}
// Read salt
salt := make([]byte, 16) // Salt size
if _, err := io.ReadFull(input, salt); err != nil {
return nil, err
}
key := argon2.IDKey(passphrase, salt, argon2TimeCost, argon2MemoryCost, argon2Threads, 32)
// Initialize AES cipher block
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// Create GCM mode with the cipher block
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
// Read nonce from the input reader
nonce := NewNonce(aesgcm.NonceSize())
if err := nonce.Read(input); err != nil {
return nil, err
}
// Initialize a buffer to store decrypted data
buf := bytes.Buffer{}
plaintext := make([]byte, aesGcmBlockSize)
// Decrypt the ciphertext in blocks
for {
// Read a block of ciphertext from the input reader
ciphertextBlock := make([]byte, aesGcmBlockSize+aesgcm.Overhead()) // Adjust block size as needed
n, err := io.ReadFull(input, ciphertextBlock)
if n == 0 {
break // end of ciphertext
}
if err != nil && !(errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF)) {
return nil, err
}
// Decrypt the block of ciphertext
plaintext, err = aesgcm.Open(plaintext[:0], nonce.Value(), ciphertextBlock[:n], nil)
if err != nil {
return nil, err
}
_, err = buf.Write(plaintext)
if err != nil {
return nil, err
}
nonce.Increment()
}
return &buf, nil
}
// aesDecryptOFB 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.
// note: This function used to decrypt files that were encrypted without a header i.e. old archives
func aesDecryptOFB(input io.Reader, passphrase []byte) (io.Reader, error) {
var emptySalt []byte = make([]byte, 0)
// 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)
@@ -59,11 +223,9 @@ func AesDecrypt(input io.Reader, passphrase []byte) (io.Reader, error) {
return nil, err
}
// If the key is unique for each ciphertext, then it's ok to use a zero
// IV.
// 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

View File

@@ -2,18 +2,28 @@ package crypto
import (
"io"
"io/ioutil"
"math/rand"
"os"
"path/filepath"
"testing"
"github.com/docker/docker/pkg/ioutils"
"github.com/stretchr/testify/assert"
)
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
func randBytes(n int) []byte {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return b
}
func Test_encryptAndDecrypt_withTheSamePassword(t *testing.T) {
tmpdir, _ := ioutils.TempDir("", "encrypt")
defer os.RemoveAll(tmpdir)
const passphrase = "passphrase"
tmpdir := t.TempDir()
var (
originFilePath = filepath.Join(tmpdir, "origin")
@@ -21,18 +31,100 @@ func Test_encryptAndDecrypt_withTheSamePassword(t *testing.T) {
decryptedFilePath = filepath.Join(tmpdir, "decrypted")
)
content := []byte("content")
ioutil.WriteFile(originFilePath, content, 0600)
content := randBytes(1024*1024*100 + 523)
os.WriteFile(originFilePath, content, 0600)
originFile, _ := os.Open(originFilePath)
defer originFile.Close()
encryptedFileWriter, _ := os.Create(encryptedFilePath)
err := AesEncrypt(originFile, encryptedFileWriter, []byte(passphrase))
assert.Nil(t, err, "Failed to encrypt a file")
encryptedFileWriter.Close()
encryptedContent, err := os.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, _ := os.ReadFile(decryptedFilePath)
assert.Equal(t, content, decryptedContent, "Original and decrypted content should match")
}
func Test_encryptAndDecrypt_withStrongPassphrase(t *testing.T) {
const passphrase = "A strong passphrase with special characters: !@#$%^&*()_+"
tmpdir := t.TempDir()
var (
originFilePath = filepath.Join(tmpdir, "origin2")
encryptedFilePath = filepath.Join(tmpdir, "encrypted2")
decryptedFilePath = filepath.Join(tmpdir, "decrypted2")
)
content := randBytes(500)
os.WriteFile(originFilePath, content, 0600)
originFile, _ := os.Open(originFilePath)
defer originFile.Close()
encryptedFileWriter, _ := os.Create(encryptedFilePath)
err := AesEncrypt(originFile, encryptedFileWriter, []byte(passphrase))
assert.Nil(t, err, "Failed to encrypt a file")
encryptedFileWriter.Close()
encryptedContent, err := os.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, _ := os.ReadFile(decryptedFilePath)
assert.Equal(t, content, decryptedContent, "Original and decrypted content should match")
}
func Test_encryptAndDecrypt_withTheSamePasswordSmallFile(t *testing.T) {
tmpdir := t.TempDir()
var (
originFilePath = filepath.Join(tmpdir, "origin2")
encryptedFilePath = filepath.Join(tmpdir, "encrypted2")
decryptedFilePath = filepath.Join(tmpdir, "decrypted2")
)
content := randBytes(500)
os.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)
encryptedFileWriter.Close()
encryptedContent, err := os.ReadFile(encryptedFilePath)
assert.Nil(t, err, "Couldn't read encrypted file")
assert.NotEqual(t, encryptedContent, content, "Content wasn't encrypted")
@@ -47,13 +139,12 @@ func Test_encryptAndDecrypt_withTheSamePassword(t *testing.T) {
io.Copy(decryptedFileWriter, decryptedReader)
decryptedContent, _ := ioutil.ReadFile(decryptedFilePath)
decryptedContent, _ := os.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)
tmpdir := t.TempDir()
var (
originFilePath = filepath.Join(tmpdir, "origin")
@@ -61,8 +152,8 @@ func Test_encryptAndDecrypt_withEmptyPassword(t *testing.T) {
decryptedFilePath = filepath.Join(tmpdir, "decrypted")
)
content := []byte("content")
ioutil.WriteFile(originFilePath, content, 0600)
content := randBytes(1024 * 50)
os.WriteFile(originFilePath, content, 0600)
originFile, _ := os.Open(originFilePath)
defer originFile.Close()
@@ -72,7 +163,7 @@ func Test_encryptAndDecrypt_withEmptyPassword(t *testing.T) {
err := AesEncrypt(originFile, encryptedFileWriter, []byte(""))
assert.Nil(t, err, "Failed to encrypt a file")
encryptedContent, err := ioutil.ReadFile(encryptedFilePath)
encryptedContent, err := os.ReadFile(encryptedFilePath)
assert.Nil(t, err, "Couldn't read encrypted file")
assert.NotEqual(t, encryptedContent, content, "Content wasn't encrypted")
@@ -87,13 +178,12 @@ func Test_encryptAndDecrypt_withEmptyPassword(t *testing.T) {
io.Copy(decryptedFileWriter, decryptedReader)
decryptedContent, _ := ioutil.ReadFile(decryptedFilePath)
decryptedContent, _ := os.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)
tmpdir := t.TempDir()
var (
originFilePath = filepath.Join(tmpdir, "origin")
@@ -101,8 +191,8 @@ func Test_decryptWithDifferentPassphrase_shouldProduceWrongResult(t *testing.T)
decryptedFilePath = filepath.Join(tmpdir, "decrypted")
)
content := []byte("content")
ioutil.WriteFile(originFilePath, content, 0600)
content := randBytes(1034)
os.WriteFile(originFilePath, content, 0600)
originFile, _ := os.Open(originFilePath)
defer originFile.Close()
@@ -112,7 +202,7 @@ func Test_decryptWithDifferentPassphrase_shouldProduceWrongResult(t *testing.T)
err := AesEncrypt(originFile, encryptedFileWriter, []byte("passphrase"))
assert.Nil(t, err, "Failed to encrypt a file")
encryptedContent, err := ioutil.ReadFile(encryptedFilePath)
encryptedContent, err := os.ReadFile(encryptedFilePath)
assert.Nil(t, err, "Couldn't read encrypted file")
assert.NotEqual(t, encryptedContent, content, "Content wasn't encrypted")
@@ -122,11 +212,6 @@ func Test_decryptWithDifferentPassphrase_shouldProduceWrongResult(t *testing.T)
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")
_, err = AesDecrypt(encryptedFileReader, []byte("garbage"))
assert.NotNil(t, err, "Should not allow decrypt with wrong passphrase")
}

View File

@@ -7,9 +7,8 @@ import (
"crypto/x509"
"encoding/base64"
"encoding/hex"
"math/big"
"github.com/portainer/libcrypto"
"github.com/portainer/portainer/pkg/libcrypto"
)
const (
@@ -115,9 +114,6 @@ func (service *ECDSAService) CreateSignature(message string) (string, error) {
hash := libcrypto.HashFromBytes([]byte(message))
r := big.NewInt(0)
s := big.NewInt(0)
r, s, err := ecdsa.Sign(rand.Reader, service.privateKey, hash)
if err != nil {
return "", err

View File

@@ -9,11 +9,11 @@ type Service struct{}
// Hash hashes a string using the bcrypt algorithm
func (*Service) Hash(data string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(data), bcrypt.DefaultCost)
bytes, err := bcrypt.GenerateFromPassword([]byte(data), bcrypt.DefaultCost)
if err != nil {
return "", nil
return "", err
}
return string(hash), nil
return string(bytes), err
}
// CompareHashAndData compares a hash to clear data and returns an error if the comparison fails.

53
api/crypto/hash_test.go Normal file
View File

@@ -0,0 +1,53 @@
package crypto
import (
"testing"
)
func TestService_Hash(t *testing.T) {
var s = &Service{}
type args struct {
hash string
data string
}
tests := []struct {
name string
args args
expect bool
}{
{
name: "Empty",
args: args{
hash: "",
data: "",
},
expect: false,
},
{
name: "Matching",
args: args{
hash: "$2a$10$6BFGd94oYx8k0bFNO6f33uPUpcpAJyg8UVX.akLe9EthF/ZBTXqcy",
data: "Passw0rd!",
},
expect: true,
},
{
name: "Not matching",
args: args{
hash: "$2a$10$ltKrUZ7492xyutHOb0/XweevU4jyw7QO66rP32jTVOMb3EX3JxA/a",
data: "Passw0rd!",
},
expect: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := s.CompareHashAndData(tt.args.hash, tt.args.data)
if (err != nil) == tt.expect {
t.Errorf("Service.CompareHashAndData() = %v", err)
}
})
}
}

61
api/crypto/nonce.go Normal file
View File

@@ -0,0 +1,61 @@
package crypto
import (
"crypto/rand"
"errors"
"io"
)
type Nonce struct {
val []byte
}
func NewNonce(size int) *Nonce {
return &Nonce{val: make([]byte, size)}
}
// NewRandomNonce generates a new initial nonce with the lower byte set to a random value
// This ensures there are plenty of nonce values availble before rolling over
// Based on ideas from the Secure Programming Cookbook for C and C++ by John Viega, Matt Messier
// https://www.oreilly.com/library/view/secure-programming-cookbook/0596003943/ch04s09.html
func NewRandomNonce(size int) (*Nonce, error) {
randomBytes := 1
if size <= randomBytes {
return nil, errors.New("nonce size must be greater than the number of random bytes")
}
randomPart := make([]byte, randomBytes)
if _, err := rand.Read(randomPart); err != nil {
return nil, err
}
zeroPart := make([]byte, size-randomBytes)
nonceVal := append(randomPart, zeroPart...)
return &Nonce{val: nonceVal}, nil
}
func (n *Nonce) Read(stream io.Reader) error {
_, err := io.ReadFull(stream, n.val)
return err
}
func (n *Nonce) Value() []byte {
return n.val
}
func (n *Nonce) Increment() error {
// Start incrementing from the least significant byte
for i := len(n.val) - 1; i >= 0; i-- {
// Increment the current byte
n.val[i]++
// Check for overflow
if n.val[i] != 0 {
// No overflow, nonce is successfully incremented
return nil
}
}
// If we reach here, it means the nonce has overflowed
return errors.New("nonce overflow")
}

View File

@@ -3,11 +3,11 @@ package crypto
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"os"
)
// CreateServerTLSConfiguration creates a basic tls.Config to be used by servers with recommended TLS settings
func CreateServerTLSConfiguration() *tls.Config {
// CreateTLSConfiguration creates a basic tls.Config with recommended TLS settings
func CreateTLSConfiguration() *tls.Config {
return &tls.Config{
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
@@ -20,6 +20,14 @@ func CreateServerTLSConfiguration() *tls.Config {
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
},
}
}
@@ -27,7 +35,7 @@ func CreateServerTLSConfiguration() *tls.Config {
// CreateTLSConfigurationFromBytes initializes a tls.Config using a CA certificate, a certificate and a key
// loaded from memory.
func CreateTLSConfigurationFromBytes(caCert, cert, key []byte, skipClientVerification, skipServerVerification bool) (*tls.Config, error) {
config := &tls.Config{}
config := CreateTLSConfiguration()
config.InsecureSkipVerify = skipServerVerification
if !skipClientVerification {
@@ -50,7 +58,7 @@ func CreateTLSConfigurationFromBytes(caCert, cert, key []byte, skipClientVerific
// CreateTLSConfigurationFromDisk initializes a tls.Config using a CA certificate, a certificate and a key
// loaded from disk.
func CreateTLSConfigurationFromDisk(caCertPath, certPath, keyPath string, skipServerVerification bool) (*tls.Config, error) {
config := &tls.Config{}
config := CreateTLSConfiguration()
config.InsecureSkipVerify = skipServerVerification
if certPath != "" && keyPath != "" {
@@ -63,7 +71,7 @@ func CreateTLSConfigurationFromDisk(caCertPath, certPath, keyPath string, skipSe
}
if !skipServerVerification && caCertPath != "" {
caCert, err := ioutil.ReadFile(caCertPath)
caCert, err := os.ReadFile(caCertPath)
if err != nil {
return nil, err
}

View File

@@ -5,13 +5,16 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"math"
"os"
"path"
"strconv"
"time"
portainer "github.com/portainer/portainer/api"
dserrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/sirupsen/logrus"
"github.com/rs/zerolog/log"
bolt "go.etcd.io/bbolt"
)
@@ -71,7 +74,6 @@ func (connection *DbConnection) IsEncryptedStore() bool {
// NeedsEncryptionMigration returns true if database encryption is enabled and
// we have an un-encrypted DB that requires migration to an encrypted DB
func (connection *DbConnection) NeedsEncryptionMigration() (bool, error) {
// Cases: Note, we need to check both portainer.db and portainer.edb
// to determine if it's a new store. We only need to differentiate between cases 2,3 and 5
@@ -119,11 +121,11 @@ func (connection *DbConnection) NeedsEncryptionMigration() (bool, error) {
// Open opens and initializes the BoltDB database.
func (connection *DbConnection) Open() error {
logrus.Infof("Loading PortainerDB: %s", connection.GetDatabaseFileName())
log.Info().Str("filename", connection.GetDatabaseFileName()).Msg("loading PortainerDB")
// Now we open the db
databasePath := connection.GetDatabaseFilePath()
db, err := bolt.Open(databasePath, 0600, &bolt.Options{
Timeout: 1 * time.Second,
InitialMmapSize: connection.InitialMmapSize,
@@ -131,26 +133,52 @@ func (connection *DbConnection) Open() error {
if err != nil {
return err
}
db.MaxBatchSize = connection.MaxBatchSize
db.MaxBatchDelay = connection.MaxBatchDelay
connection.DB = db
return nil
}
// Close closes the BoltDB database.
// Safe to being called multiple times.
func (connection *DbConnection) Close() error {
log.Info().Msg("closing PortainerDB")
if connection.DB != nil {
return connection.DB.Close()
}
return nil
}
func (connection *DbConnection) txFn(fn func(portainer.Transaction) error) func(*bolt.Tx) error {
return func(tx *bolt.Tx) error {
return fn(&DbTransaction{conn: connection, tx: tx})
}
}
// UpdateTx executes the given function inside a read-write transaction
func (connection *DbConnection) UpdateTx(fn func(portainer.Transaction) error) error {
if connection.MaxBatchDelay > 0 && connection.MaxBatchSize > 1 {
return connection.Batch(connection.txFn(fn))
}
return connection.Update(connection.txFn(fn))
}
// ViewTx executes the given function inside a read-only transaction
func (connection *DbConnection) ViewTx(fn func(portainer.Transaction) error) error {
return connection.View(connection.txFn(fn))
}
// BackupTo backs up db to a provided writer.
// It does hot backup and doesn't block other database reads and writes
func (connection *DbConnection) BackupTo(w io.Writer) error {
return connection.View(func(tx *bolt.Tx) error {
_, err := tx.WriteTo(w)
return err
})
}
@@ -158,14 +186,15 @@ func (connection *DbConnection) BackupTo(w io.Writer) error {
func (connection *DbConnection) ExportRaw(filename string) error {
databasePath := connection.GetDatabaseFilePath()
if _, err := os.Stat(databasePath); err != nil {
return fmt.Errorf("stat on %s failed: %s", databasePath, err)
return fmt.Errorf("stat on %s failed, error: %w", databasePath, err)
}
b, err := connection.exportJson(databasePath)
b, err := connection.ExportJSON(databasePath, true)
if err != nil {
return err
}
return ioutil.WriteFile(filename, b, 0600)
return os.WriteFile(filename, b, 0600)
}
// ConvertToKey returns an 8-byte big endian representation of v.
@@ -174,39 +203,36 @@ func (connection *DbConnection) ExportRaw(filename string) error {
func (connection *DbConnection) ConvertToKey(v int) []byte {
b := make([]byte, 8)
binary.BigEndian.PutUint64(b, uint64(v))
return b
}
// CreateBucket is a generic function used to create a bucket inside a database database.
// keyToString Converts a key to a string value suitable for logging
func keyToString(b []byte) string {
if len(b) != 8 {
return string(b)
}
v := binary.BigEndian.Uint64(b)
if v <= math.MaxInt32 {
return strconv.FormatUint(v, 10)
}
return string(b)
}
// CreateBucket is a generic function used to create a bucket inside a database.
func (connection *DbConnection) SetServiceName(bucketName string) error {
return connection.Batch(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte(bucketName))
return err
return connection.UpdateTx(func(tx portainer.Transaction) error {
return tx.SetServiceName(bucketName)
})
}
// GetObject is a generic function used to retrieve an unmarshalled object from a database database.
func (connection *DbConnection) GetObject(bucketName string, key []byte, object interface{}) error {
var data []byte
err := connection.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
value := bucket.Get(key)
if value == nil {
return dserrors.ErrObjectNotFound
}
data = make([]byte, len(value))
copy(data, value)
return nil
// GetObject is a generic function used to retrieve an unmarshalled object from a database.
func (connection *DbConnection) GetObject(bucketName string, key []byte, object any) error {
return connection.ViewTx(func(tx portainer.Transaction) error {
return tx.GetObject(bucketName, key, object)
})
if err != nil {
return err
}
return connection.UnmarshalObjectWithJsoniter(data, object)
}
func (connection *DbConnection) getEncryptionKey() []byte {
@@ -217,50 +243,51 @@ func (connection *DbConnection) getEncryptionKey() []byte {
return connection.EncryptionKey
}
// UpdateObject is a generic function used to update an object inside a database database.
func (connection *DbConnection) UpdateObject(bucketName string, key []byte, object interface{}) error {
data, err := connection.MarshalObject(object)
if err != nil {
return err
}
// UpdateObject is a generic function used to update an object inside a database.
func (connection *DbConnection) UpdateObject(bucketName string, key []byte, object any) error {
return connection.UpdateTx(func(tx portainer.Transaction) error {
return tx.UpdateObject(bucketName, key, object)
})
}
// UpdateObjectFunc is a generic function used to update an object safely without race conditions.
func (connection *DbConnection) UpdateObjectFunc(bucketName string, key []byte, object any, updateFn func()) error {
return connection.Batch(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
data := bucket.Get(key)
if data == nil {
return fmt.Errorf("%w (bucket=%s, key=%s)", dserrors.ErrObjectNotFound, bucketName, keyToString(key))
}
err := connection.UnmarshalObject(data, object)
if err != nil {
return err
}
updateFn()
data, err = connection.MarshalObject(object)
if err != nil {
return err
}
return bucket.Put(key, data)
})
}
// DeleteObject is a generic function used to delete an object inside a database database.
// DeleteObject is a generic function used to delete an object inside a database.
func (connection *DbConnection) DeleteObject(bucketName string, key []byte) error {
return connection.Batch(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
return bucket.Delete(key)
return connection.UpdateTx(func(tx portainer.Transaction) error {
return tx.DeleteObject(bucketName, key)
})
}
// DeleteAllObjects delete all objects where matching() returns (id, ok).
// TODO: think about how to return the error inside (maybe change ok to type err, and use "notfound"?
func (connection *DbConnection) DeleteAllObjects(bucketName string, matching func(o interface{}) (id int, ok bool)) error {
return connection.Batch(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 obj interface{}
err := connection.UnmarshalObject(v, &obj)
if err != nil {
return err
}
if id, ok := matching(obj); ok {
err := bucket.Delete(connection.ConvertToKey(id))
if err != nil {
return err
}
}
}
return nil
func (connection *DbConnection) DeleteAllObjects(bucketName string, obj any, matching func(o any) (id int, ok bool)) error {
return connection.UpdateTx(func(tx portainer.Transaction) error {
return tx.DeleteAllObjects(bucketName, obj, matching)
})
}
@@ -268,13 +295,8 @@ func (connection *DbConnection) DeleteAllObjects(bucketName string, matching fun
func (connection *DbConnection) GetNextIdentifier(bucketName string) int {
var identifier int
connection.Batch(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
id, err := bucket.NextSequence()
if err != nil {
return err
}
identifier = int(id)
_ = connection.UpdateTx(func(tx portainer.Transaction) error {
identifier = tx.GetNextIdentifier(bucketName)
return nil
})
@@ -282,136 +304,64 @@ func (connection *DbConnection) GetNextIdentifier(bucketName string) int {
}
// CreateObject creates a new object in the bucket, using the next bucket sequence id
func (connection *DbConnection) CreateObject(bucketName string, fn func(uint64) (int, interface{})) error {
return connection.Batch(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
seqId, _ := bucket.NextSequence()
id, obj := fn(seqId)
data, err := connection.MarshalObject(obj)
if err != nil {
return err
}
return bucket.Put(connection.ConvertToKey(int(id)), data)
func (connection *DbConnection) CreateObject(bucketName string, fn func(uint64) (int, any)) error {
return connection.UpdateTx(func(tx portainer.Transaction) error {
return tx.CreateObject(bucketName, fn)
})
}
// CreateObjectWithId creates a new object in the bucket, using the specified id
func (connection *DbConnection) CreateObjectWithId(bucketName string, id int, obj interface{}) error {
return connection.Batch(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
data, err := connection.MarshalObject(obj)
if err != nil {
return err
}
return bucket.Put(connection.ConvertToKey(id), data)
func (connection *DbConnection) CreateObjectWithId(bucketName string, id int, obj any) error {
return connection.UpdateTx(func(tx portainer.Transaction) error {
return tx.CreateObjectWithId(bucketName, id, obj)
})
}
// CreateObjectWithStringId creates a new object in the bucket, using the specified id
func (connection *DbConnection) CreateObjectWithStringId(bucketName string, id []byte, obj interface{}) error {
return connection.Batch(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
data, err := connection.MarshalObject(obj)
if err != nil {
return err
}
return bucket.Put(id, data)
func (connection *DbConnection) CreateObjectWithStringId(bucketName string, id []byte, obj any) error {
return connection.UpdateTx(func(tx portainer.Transaction) error {
return tx.CreateObjectWithStringId(bucketName, id, obj)
})
}
// CreateObjectWithSetSequence creates a new object in the bucket, using the specified id, and sets the bucket sequence
// avoid this :)
func (connection *DbConnection) CreateObjectWithSetSequence(bucketName string, id int, obj interface{}) error {
return connection.Batch(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(bucketName))
// We manually manage sequences for schedules
err := bucket.SetSequence(uint64(id))
if err != nil {
return err
}
data, err := connection.MarshalObject(obj)
if err != nil {
return err
}
return bucket.Put(connection.ConvertToKey(id), data)
func (connection *DbConnection) GetAll(bucketName string, obj any, appendFn func(o any) (any, error)) error {
return connection.ViewTx(func(tx portainer.Transaction) error {
return tx.GetAll(bucketName, obj, appendFn)
})
}
func (connection *DbConnection) GetAll(bucketName string, obj interface{}, append func(o interface{}) (interface{}, error)) error {
err := 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() {
err := connection.UnmarshalObject(v, obj)
if err != nil {
return err
}
obj, err = append(obj)
if err != nil {
return err
}
}
return nil
func (connection *DbConnection) GetAllWithKeyPrefix(bucketName string, keyPrefix []byte, obj any, appendFn func(o any) (any, error)) error {
return connection.ViewTx(func(tx portainer.Transaction) error {
return tx.GetAllWithKeyPrefix(bucketName, keyPrefix, obj, appendFn)
})
return err
}
// TODO: decide which Unmarshal to use, and use one...
func (connection *DbConnection) GetAllWithJsoniter(bucketName string, obj interface{}, append func(o interface{}) (interface{}, error)) error {
err := 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() {
err := connection.UnmarshalObjectWithJsoniter(v, obj)
if err != nil {
return err
}
obj, err = append(obj)
if err != nil {
return err
}
}
return nil
})
return err
}
func (connection *DbConnection) BackupMetadata() (map[string]interface{}, error) {
buckets := map[string]interface{}{}
// BackupMetadata will return a copy of the boltdb sequence numbers for all buckets.
func (connection *DbConnection) BackupMetadata() (map[string]any, error) {
buckets := map[string]any{}
err := connection.View(func(tx *bolt.Tx) error {
err := tx.ForEach(func(name []byte, bucket *bolt.Bucket) error {
return tx.ForEach(func(name []byte, bucket *bolt.Bucket) error {
bucketName := string(name)
bucket = tx.Bucket([]byte(bucketName))
seqId := bucket.Sequence()
buckets[bucketName] = int(seqId)
return nil
})
return err
})
return buckets, err
}
func (connection *DbConnection) RestoreMetadata(s map[string]interface{}) error {
// RestoreMetadata will restore the boltdb sequence numbers for all buckets.
func (connection *DbConnection) RestoreMetadata(s map[string]any) error {
var err error
for bucketName, v := range s {
id, ok := v.(float64) // JSON ints are unmarshalled to interface as float64. See: https://pkg.go.dev/encoding/json#Decoder.Decode
if !ok {
logrus.Errorf("Failed to restore metadata to bucket %s, skipped", bucketName)
log.Error().Str("bucket", bucketName).Msg("failed to restore metadata to bucket, skipped")
continue
}
@@ -420,6 +370,7 @@ func (connection *DbConnection) RestoreMetadata(s map[string]interface{}) error
if err != nil {
return err
}
return bucket.SetSequence(uint64(id))
})
}

View File

@@ -87,10 +87,7 @@ func Test_NeedsEncryptionMigration(t *testing.T) {
}
for _, tc := range cases {
tc := tc
t.Run(tc.name, func(t *testing.T) {
connection := DbConnection{Path: dir}
if tc.dbname == "both" {

View File

@@ -1,17 +1,37 @@
package boltdb
import (
"encoding/json"
"time"
"github.com/sirupsen/logrus"
"github.com/rs/zerolog/log"
"github.com/segmentio/encoding/json"
bolt "go.etcd.io/bbolt"
)
func backupMetadata(connection *bolt.DB) (map[string]any, error) {
buckets := map[string]any{}
err := connection.View(func(tx *bolt.Tx) error {
err := tx.ForEach(func(name []byte, bucket *bolt.Bucket) error {
bucketName := string(name)
seqId := bucket.Sequence()
buckets[bucketName] = int(seqId)
return nil
})
return err
})
return buckets, err
}
// ExportJSON creates a JSON representation from a DbConnection. You can include
// the database's metadata or ignore it. Ensure the database is closed before
// using this function.
// inspired by github.com/konoui/boltdb-exporter (which has no license)
// but very much simplified, based on how we use boltdb
func (c *DbConnection) exportJson(databasePath string) ([]byte, error) {
logrus.WithField("databasePath", databasePath).Infof("exportJson")
func (c *DbConnection) ExportJSON(databasePath string, metadata bool) ([]byte, error) {
log.Debug().Str("databasePath", databasePath).Msg("exportJson")
connection, err := bolt.Open(databasePath, 0600, &bolt.Options{Timeout: 1 * time.Second, ReadOnly: true})
if err != nil {
@@ -19,47 +39,71 @@ func (c *DbConnection) exportJson(databasePath string) ([]byte, error) {
}
defer connection.Close()
backup := make(map[string]interface{})
backup := make(map[string]any)
if metadata {
meta, err := backupMetadata(connection)
if err != nil {
log.Error().Err(err).Msg("failed exporting metadata")
}
backup["__metadata"] = meta
}
err = connection.View(func(tx *bolt.Tx) error {
err = tx.ForEach(func(name []byte, bucket *bolt.Bucket) error {
bucketName := string(name)
var list []interface{}
var list []any
version := make(map[string]string)
cursor := bucket.Cursor()
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
if v == nil {
continue
}
var obj interface{}
var obj any
err := c.UnmarshalObject(v, &obj)
if err != nil {
logrus.WithError(err).Errorf("Failed to unmarshal (bucket %s): %v", bucketName, string(v))
log.Error().
Str("bucket", bucketName).
Str("object", string(v)).
Err(err).
Msg("failed to unmarshal")
obj = v
}
if bucketName == "version" {
version[string(k)] = string(v)
} else {
list = append(list, obj)
}
}
if bucketName == "version" {
backup[bucketName] = version
return nil
}
if len(list) > 0 {
if bucketName == "ssl" ||
bucketName == "settings" ||
bucketName == "tunnel_server" {
backup[bucketName] = list[0]
backup[bucketName] = nil
if len(list) > 0 {
backup[bucketName] = list[0]
}
return nil
}
backup[bucketName] = list
return nil
}
return nil
})
return err
})
if err != nil {
return []byte("{}"), err
}

View File

@@ -1,38 +1,44 @@
package boltdb
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/json"
"fmt"
"io"
jsoniter "github.com/json-iterator/go"
"github.com/pkg/errors"
"github.com/segmentio/encoding/json"
)
var errEncryptedStringTooShort = fmt.Errorf("encrypted string too short")
var errEncryptedStringTooShort = errors.New("encrypted string too short")
// MarshalObject encodes an object to binary format
func (connection *DbConnection) MarshalObject(object interface{}) (data []byte, err error) {
func (connection *DbConnection) MarshalObject(object any) ([]byte, error) {
buf := &bytes.Buffer{}
// Special case for the VERSION bucket. Here we're not using json
if v, ok := object.(string); ok {
data = []byte(v)
buf.WriteString(v)
} else {
data, err = json.Marshal(object)
if err != nil {
return data, err
enc := json.NewEncoder(buf)
enc.SetSortMapKeys(false)
enc.SetAppendNewline(false)
if err := enc.Encode(object); err != nil {
return nil, err
}
}
if connection.getEncryptionKey() == nil {
return data, nil
return buf.Bytes(), nil
}
return encrypt(data, connection.getEncryptionKey())
return encrypt(buf.Bytes(), connection.getEncryptionKey())
}
// UnmarshalObject decodes an object from binary data
func (connection *DbConnection) UnmarshalObject(data []byte, object interface{}) error {
func (connection *DbConnection) UnmarshalObject(data []byte, object any) error {
var err error
if connection.getEncryptionKey() != nil {
data, err = decrypt(data, connection.getEncryptionKey())
@@ -40,8 +46,8 @@ func (connection *DbConnection) UnmarshalObject(data []byte, object interface{})
return errors.Wrap(err, "Failed decrypting object")
}
}
e := json.Unmarshal(data, object)
if e != nil {
if e := json.Unmarshal(data, object); e != nil {
// Special case for the VERSION bucket. Here we're not using json
// So we need to return it as a string
s, ok := object.(*string)
@@ -51,34 +57,10 @@ func (connection *DbConnection) UnmarshalObject(data []byte, object interface{})
*s = string(data)
}
return err
}
// UnmarshalObjectWithJsoniter decodes an object from binary data
// using the jsoniter library. It is mainly used to accelerate environment(endpoint)
// decoding at the moment.
func (connection *DbConnection) UnmarshalObjectWithJsoniter(data []byte, object interface{}) error {
if connection.getEncryptionKey() != nil {
var err error
data, err = decrypt(data, connection.getEncryptionKey())
if err != nil {
return err
}
}
var jsoni = jsoniter.ConfigCompatibleWithStandardLibrary
err := jsoni.Unmarshal(data, &object)
if err != nil {
if s, ok := object.(*string); ok {
*s = string(data)
return nil
}
return err
}
return nil
}
// mmm, don't have a KMS .... aes GCM seems the most likely from
// https://gist.github.com/atoponce/07d8d4c833873be2f68c34f9afc5a78a#symmetric-encryption
@@ -88,22 +70,20 @@ func encrypt(plaintext []byte, passphrase []byte) (encrypted []byte, err error)
if err != nil {
return encrypted, err
}
nonce := make([]byte, gcm.NonceSize())
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return encrypted, err
}
ciphertextByte := gcm.Seal(
nonce,
nonce,
plaintext,
nil)
return ciphertextByte, nil
return gcm.Seal(nonce, nonce, plaintext, nil), nil
}
func decrypt(encrypted []byte, passphrase []byte) (plaintextByte []byte, err error) {
if string(encrypted) == "false" {
return []byte("false"), nil
}
block, err := aes.NewCipher(passphrase)
if err != nil {
return encrypted, errors.Wrap(err, "Error creating cypher block")
@@ -120,11 +100,8 @@ func decrypt(encrypted []byte, passphrase []byte) (plaintextByte []byte, err err
}
nonce, ciphertextByteClean := encrypted[:nonceSize], encrypted[nonceSize:]
plaintextByte, err = gcm.Open(
nil,
nonce,
ciphertextByteClean,
nil)
plaintextByte, err = gcm.Open(nil, nonce, ciphertextByteClean, nil)
if err != nil {
return encrypted, errors.Wrap(err, "Error decrypting text")
}

View File

@@ -10,7 +10,7 @@ import (
)
const (
jsonobject = `{"LogoURL":"","BlackListedLabels":[],"AuthenticationMethod":1,"LDAPSettings":{"AnonymousMode":true,"ReaderDN":"","URL":"","TLSConfig":{"TLS":false,"TLSSkipVerify":false},"StartTLS":false,"SearchSettings":[{"BaseDN":"","Filter":"","UserNameAttribute":""}],"GroupSearchSettings":[{"GroupBaseDN":"","GroupFilter":"","GroupAttribute":""}],"AutoCreateUsers":true},"OAuthSettings":{"ClientID":"","AccessTokenURI":"","AuthorizationURI":"","ResourceURI":"","RedirectURI":"","UserIdentifier":"","Scopes":"","OAuthAutoCreateUsers":false,"DefaultTeamID":0,"SSO":true,"LogoutURI":"","KubeSecretKey":"j0zLVtY/lAWBk62ByyF0uP80SOXaitsABP0TTJX8MhI="},"OpenAMTConfiguration":{"Enabled":false,"MPSServer":"","MPSUser":"","MPSPassword":"","MPSToken":"","CertFileContent":"","CertFileName":"","CertFilePassword":"","DomainName":""},"FeatureFlagSettings":{},"SnapshotInterval":"5m","TemplatesURL":"https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json","EdgeAgentCheckinInterval":5,"EnableEdgeComputeFeatures":false,"UserSessionTimeout":"8h","KubeconfigExpiry":"0","EnableTelemetry":true,"HelmRepositoryURL":"https://charts.bitnami.com/bitnami","KubectlShellImage":"portainer/kubectl-shell","DisplayDonationHeader":false,"DisplayExternalContributors":false,"EnableHostManagementFeatures":false,"AllowVolumeBrowserForRegularUsers":false,"AllowBindMountsForRegularUsers":false,"AllowPrivilegedModeForRegularUsers":false,"AllowHostNamespaceForRegularUsers":false,"AllowStackManagementForRegularUsers":false,"AllowDeviceMappingForRegularUsers":false,"AllowContainerCapabilitiesForRegularUsers":false}`
jsonobject = `{"LogoURL":"","BlackListedLabels":[],"AuthenticationMethod":1,"InternalAuthSettings": {"RequiredPasswordLength": 12}"LDAPSettings":{"AnonymousMode":true,"ReaderDN":"","URL":"","TLSConfig":{"TLS":false,"TLSSkipVerify":false},"StartTLS":false,"SearchSettings":[{"BaseDN":"","Filter":"","UserNameAttribute":""}],"GroupSearchSettings":[{"GroupBaseDN":"","GroupFilter":"","GroupAttribute":""}],"AutoCreateUsers":true},"OAuthSettings":{"ClientID":"","AccessTokenURI":"","AuthorizationURI":"","ResourceURI":"","RedirectURI":"","UserIdentifier":"","Scopes":"","OAuthAutoCreateUsers":false,"DefaultTeamID":0,"SSO":true,"LogoutURI":"","KubeSecretKey":"j0zLVtY/lAWBk62ByyF0uP80SOXaitsABP0TTJX8MhI="},"OpenAMTConfiguration":{"Enabled":false,"MPSServer":"","MPSUser":"","MPSPassword":"","MPSToken":"","CertFileContent":"","CertFileName":"","CertFilePassword":"","DomainName":""},"FeatureFlagSettings":{},"SnapshotInterval":"5m","TemplatesURL":"https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json","EdgeAgentCheckinInterval":5,"EnableEdgeComputeFeatures":false,"UserSessionTimeout":"8h","KubeconfigExpiry":"0","EnableTelemetry":true,"HelmRepositoryURL":"https://charts.bitnami.com/bitnami","KubectlShellImage":"portainer/kubectl-shell","DisplayDonationHeader":false,"DisplayExternalContributors":false,"EnableHostManagementFeatures":false,"AllowVolumeBrowserForRegularUsers":false,"AllowBindMountsForRegularUsers":false,"AllowPrivilegedModeForRegularUsers":false,"AllowHostNamespaceForRegularUsers":false,"AllowStackManagementForRegularUsers":false,"AllowDeviceMappingForRegularUsers":false,"AllowContainerCapabilitiesForRegularUsers":false}`
passphrase = "my secret key"
)
@@ -25,7 +25,7 @@ func Test_MarshalObjectUnencrypted(t *testing.T) {
uuid := uuid.Must(uuid.NewV4())
tests := []struct {
object interface{}
object any
expected string
}{
{
@@ -57,7 +57,7 @@ func Test_MarshalObjectUnencrypted(t *testing.T) {
expected: uuid.String(),
},
{
object: map[string]interface{}{"key": "value"},
object: map[string]any{"key": "value"},
expected: `{"key":"value"}`,
},
{
@@ -73,11 +73,11 @@ func Test_MarshalObjectUnencrypted(t *testing.T) {
expected: `["1","2","3"]`,
},
{
object: []map[string]interface{}{{"key1": "value1"}, {"key2": "value2"}},
object: []map[string]any{{"key1": "value1"}, {"key2": "value2"}},
expected: `[{"key1":"value1"},{"key2":"value2"}]`,
},
{
object: []interface{}{1, "2", false, map[string]interface{}{"key1": "value1"}},
object: []any{1, "2", false, map[string]any{"key1": "value1"}},
expected: `[1,"2",false,{"key1":"value1"}]`,
},
}
@@ -129,7 +129,7 @@ func Test_UnMarshalObjectUnencrypted(t *testing.T) {
var object string
err := conn.UnmarshalObject(test.object, &object)
is.NoError(err)
is.Equal(test.expected, string(object))
is.Equal(test.expected, object)
})
}
}

151
api/database/boltdb/tx.go Normal file
View File

@@ -0,0 +1,151 @@
package boltdb
import (
"bytes"
"fmt"
dserrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/rs/zerolog/log"
bolt "go.etcd.io/bbolt"
)
type DbTransaction struct {
conn *DbConnection
tx *bolt.Tx
}
func (tx *DbTransaction) SetServiceName(bucketName string) error {
_, err := tx.tx.CreateBucketIfNotExists([]byte(bucketName))
return err
}
func (tx *DbTransaction) GetObject(bucketName string, key []byte, object any) error {
bucket := tx.tx.Bucket([]byte(bucketName))
value := bucket.Get(key)
if value == nil {
return fmt.Errorf("%w (bucket=%s, key=%s)", dserrors.ErrObjectNotFound, bucketName, keyToString(key))
}
return tx.conn.UnmarshalObject(value, object)
}
func (tx *DbTransaction) UpdateObject(bucketName string, key []byte, object any) error {
data, err := tx.conn.MarshalObject(object)
if err != nil {
return err
}
bucket := tx.tx.Bucket([]byte(bucketName))
return bucket.Put(key, data)
}
func (tx *DbTransaction) DeleteObject(bucketName string, key []byte) error {
bucket := tx.tx.Bucket([]byte(bucketName))
return bucket.Delete(key)
}
func (tx *DbTransaction) DeleteAllObjects(bucketName string, obj any, matchingFn func(o any) (id int, ok bool)) error {
var ids []int
bucket := tx.tx.Bucket([]byte(bucketName))
cursor := bucket.Cursor()
for k, v := cursor.First(); k != nil; k, v = cursor.Next() {
err := tx.conn.UnmarshalObject(v, &obj)
if err != nil {
return err
}
if id, ok := matchingFn(obj); ok {
ids = append(ids, id)
}
}
for _, id := range ids {
if err := bucket.Delete(tx.conn.ConvertToKey(id)); err != nil {
return err
}
}
return nil
}
func (tx *DbTransaction) GetNextIdentifier(bucketName string) int {
bucket := tx.tx.Bucket([]byte(bucketName))
id, err := bucket.NextSequence()
if err != nil {
log.Error().Err(err).Str("bucket", bucketName).Msg("failed to get the next identifier")
return 0
}
return int(id)
}
func (tx *DbTransaction) CreateObject(bucketName string, fn func(uint64) (int, any)) error {
bucket := tx.tx.Bucket([]byte(bucketName))
seqId, _ := bucket.NextSequence()
id, obj := fn(seqId)
data, err := tx.conn.MarshalObject(obj)
if err != nil {
return err
}
return bucket.Put(tx.conn.ConvertToKey(id), data)
}
func (tx *DbTransaction) CreateObjectWithId(bucketName string, id int, obj any) error {
bucket := tx.tx.Bucket([]byte(bucketName))
data, err := tx.conn.MarshalObject(obj)
if err != nil {
return err
}
return bucket.Put(tx.conn.ConvertToKey(id), data)
}
func (tx *DbTransaction) CreateObjectWithStringId(bucketName string, id []byte, obj any) error {
bucket := tx.tx.Bucket([]byte(bucketName))
data, err := tx.conn.MarshalObject(obj)
if err != nil {
return err
}
return bucket.Put(id, data)
}
func (tx *DbTransaction) GetAll(bucketName string, obj any, appendFn func(o any) (any, error)) error {
bucket := tx.tx.Bucket([]byte(bucketName))
return bucket.ForEach(func(k []byte, v []byte) error {
err := tx.conn.UnmarshalObject(v, obj)
if err == nil {
obj, err = appendFn(obj)
}
return err
})
}
func (tx *DbTransaction) GetAllWithKeyPrefix(bucketName string, keyPrefix []byte, obj any, appendFn func(o any) (any, error)) error {
cursor := tx.tx.Bucket([]byte(bucketName)).Cursor()
for k, v := cursor.Seek(keyPrefix); k != nil && bytes.HasPrefix(k, keyPrefix); k, v = cursor.Next() {
err := tx.conn.UnmarshalObject(v, obj)
if err != nil {
return err
}
obj, err = appendFn(obj)
if err != nil {
return err
}
}
return nil
}

View File

@@ -0,0 +1,126 @@
package boltdb
import (
"errors"
"testing"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
)
const testBucketName = "test-bucket"
const testId = 1234
type testStruct struct {
Key string
Value string
}
func TestTxs(t *testing.T) {
conn := DbConnection{
Path: t.TempDir(),
}
err := conn.Open()
if err != nil {
t.Fatal(err)
}
defer conn.Close()
// Error propagation
err = conn.UpdateTx(func(tx portainer.Transaction) error {
return errors.New("this is an error")
})
if err == nil {
t.Fatal("an error was expected, got nil instead")
}
// Create an object
newObj := testStruct{
Key: "key",
Value: "value",
}
err = conn.UpdateTx(func(tx portainer.Transaction) error {
err = tx.SetServiceName(testBucketName)
if err != nil {
return err
}
return tx.CreateObjectWithId(testBucketName, testId, newObj)
})
if err != nil {
t.Fatal(err)
}
obj := testStruct{}
err = conn.ViewTx(func(tx portainer.Transaction) error {
return tx.GetObject(testBucketName, conn.ConvertToKey(testId), &obj)
})
if err != nil {
t.Fatal(err)
}
if obj.Key != newObj.Key || obj.Value != newObj.Value {
t.Fatalf("expected %s:%s, got %s:%s instead", newObj.Key, newObj.Value, obj.Key, obj.Value)
}
// Update an object
updatedObj := testStruct{
Key: "updated-key",
Value: "updated-value",
}
err = conn.UpdateTx(func(tx portainer.Transaction) error {
return tx.UpdateObject(testBucketName, conn.ConvertToKey(testId), &updatedObj)
})
err = conn.ViewTx(func(tx portainer.Transaction) error {
return tx.GetObject(testBucketName, conn.ConvertToKey(testId), &obj)
})
if err != nil {
t.Fatal(err)
}
if obj.Key != updatedObj.Key || obj.Value != updatedObj.Value {
t.Fatalf("expected %s:%s, got %s:%s instead", updatedObj.Key, updatedObj.Value, obj.Key, obj.Value)
}
// Delete an object
err = conn.UpdateTx(func(tx portainer.Transaction) error {
return tx.DeleteObject(testBucketName, conn.ConvertToKey(testId))
})
if err != nil {
t.Fatal(err)
}
err = conn.ViewTx(func(tx portainer.Transaction) error {
return tx.GetObject(testBucketName, conn.ConvertToKey(testId), &obj)
})
if !dataservices.IsErrObjectNotFound(err) {
t.Fatal(err)
}
// Get next identifier
err = conn.UpdateTx(func(tx portainer.Transaction) error {
id1 := tx.GetNextIdentifier(testBucketName)
id2 := tx.GetNextIdentifier(testBucketName)
if id1+1 != id2 {
return errors.New("unexpected identifier sequence")
}
return nil
})
if err != nil {
t.Fatal(err)
}
// Try to write in a read transaction
err = conn.ViewTx(func(tx portainer.Transaction) error {
return tx.CreateObjectWithId(testBucketName, testId, newObj)
})
if err == nil {
t.Fatal("an error was expected, got nil instead")
}
}

View File

@@ -9,12 +9,12 @@ import (
// NewDatabase should use config options to return a connection to the requested database
func NewDatabase(storeType, storePath string, encryptionKey []byte) (connection portainer.Connection, err error) {
switch storeType {
case "boltdb":
if storeType == "boltdb" {
return &boltdb.DbConnection{
Path: storePath,
EncryptionKey: encryptionKey,
}, nil
}
return nil, fmt.Errorf("unknown storage database: %s", storeType)
return nil, fmt.Errorf("Unknown storage database: %s", storeType)
}

View File

@@ -0,0 +1,8 @@
package models
type Version struct {
SchemaVersion string
MigratorCount int
Edition int
InstanceID string
}

View File

@@ -1,22 +1,22 @@
package apikeyrepository
import (
"bytes"
"errors"
"fmt"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices/errors"
"github.com/sirupsen/logrus"
"github.com/portainer/portainer/api/dataservices"
dserrors "github.com/portainer/portainer/api/dataservices/errors"
"github.com/rs/zerolog/log"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "api_key"
)
// BucketName represents the name of the bucket where this service stores data.
const BucketName = "api_key"
// Service represents a service for managing api-key data.
type Service struct {
connection portainer.Connection
dataservices.BaseDataService[portainer.APIKey, portainer.APIKeyID]
}
// NewService creates a new instance of a service.
@@ -27,26 +27,31 @@ func NewService(connection portainer.Connection) (*Service, error) {
}
return &Service{
connection: connection,
BaseDataService: dataservices.BaseDataService[portainer.APIKey, portainer.APIKeyID]{
Bucket: BucketName,
Connection: connection,
},
}, nil
}
// GetAPIKeysByUserID returns a slice containing all the APIKeys a user has access to.
func (service *Service) GetAPIKeysByUserID(userID portainer.UserID) ([]portainer.APIKey, error) {
var result = make([]portainer.APIKey, 0)
result := make([]portainer.APIKey, 0)
err := service.connection.GetAll(
err := service.Connection.GetAll(
BucketName,
&portainer.APIKey{},
func(obj interface{}) (interface{}, error) {
func(obj any) (any, error) {
record, ok := obj.(*portainer.APIKey)
if !ok {
logrus.WithField("obj", obj).Errorf("Failed to convert to APIKey object")
return nil, fmt.Errorf("Failed to convert to APIKey object: %s", obj)
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to APIKey object")
return nil, fmt.Errorf("failed to convert to APIKey object: %s", obj)
}
if record.UserID == userID {
result = append(result, *record)
}
return &portainer.APIKey{}, nil
})
@@ -55,65 +60,45 @@ func (service *Service) GetAPIKeysByUserID(userID portainer.UserID) ([]portainer
// GetAPIKeyByDigest returns the API key for the associated digest.
// Note: there is a 1-to-1 mapping of api-key and digest
func (service *Service) GetAPIKeyByDigest(digest []byte) (*portainer.APIKey, error) {
func (service *Service) GetAPIKeyByDigest(digest string) (*portainer.APIKey, error) {
var k *portainer.APIKey
stop := fmt.Errorf("ok")
err := service.connection.GetAll(
err := service.Connection.GetAll(
BucketName,
&portainer.APIKey{},
func(obj interface{}) (interface{}, error) {
func(obj any) (any, error) {
key, ok := obj.(*portainer.APIKey)
if !ok {
logrus.WithField("obj", obj).Errorf("Failed to convert to APIKey object")
return nil, fmt.Errorf("Failed to convert to APIKey object: %s", obj)
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to APIKey object")
return nil, fmt.Errorf("failed to convert to APIKey object: %s", obj)
}
if bytes.Equal(key.Digest, digest) {
if key.Digest == digest {
k = key
return nil, stop
}
return &portainer.APIKey{}, nil
})
if err == stop {
if errors.Is(err, stop) {
return k, nil
}
if err == nil {
return nil, errors.ErrObjectNotFound
return nil, dserrors.ErrObjectNotFound
}
return nil, err
}
// CreateAPIKey creates a new APIKey object.
func (service *Service) CreateAPIKey(record *portainer.APIKey) error {
return service.connection.CreateObject(
// Create creates a new APIKey object.
func (service *Service) Create(record *portainer.APIKey) error {
return service.Connection.CreateObject(
BucketName,
func(id uint64) (int, interface{}) {
func(id uint64) (int, any) {
record.ID = portainer.APIKeyID(id)
return int(record.ID), record
},
)
}
// GetAPIKey retrieves an existing APIKey object by api key ID.
func (service *Service) GetAPIKey(keyID portainer.APIKeyID) (*portainer.APIKey, error) {
var key portainer.APIKey
identifier := service.connection.ConvertToKey(int(keyID))
err := service.connection.GetObject(BucketName, identifier, &key)
if err != nil {
return nil, err
}
return &key, nil
}
func (service *Service) UpdateAPIKey(key *portainer.APIKey) error {
identifier := service.connection.ConvertToKey(int(key.ID))
return service.connection.UpdateObject(BucketName, identifier, key)
}
func (service *Service) DeleteAPIKey(ID portainer.APIKeyID) error {
identifier := service.connection.ConvertToKey(int(ID))
return service.connection.DeleteObject(BucketName, identifier)
}

66
api/dataservices/base.go Normal file
View File

@@ -0,0 +1,66 @@
package dataservices
import (
portainer "github.com/portainer/portainer/api"
"golang.org/x/exp/constraints"
)
type BaseCRUD[T any, I constraints.Integer] interface {
Create(element *T) error
Read(ID I) (*T, error)
ReadAll() ([]T, error)
Update(ID I, element *T) error
Delete(ID I) error
}
type BaseDataService[T any, I constraints.Integer] struct {
Bucket string
Connection portainer.Connection
}
func (s *BaseDataService[T, I]) BucketName() string {
return s.Bucket
}
func (service *BaseDataService[T, I]) Tx(tx portainer.Transaction) BaseDataServiceTx[T, I] {
return BaseDataServiceTx[T, I]{
Bucket: service.Bucket,
Connection: service.Connection,
Tx: tx,
}
}
func (service BaseDataService[T, I]) Read(ID I) (*T, error) {
var element *T
return element, service.Connection.ViewTx(func(tx portainer.Transaction) error {
var err error
element, err = service.Tx(tx).Read(ID)
return err
})
}
func (service BaseDataService[T, I]) ReadAll() ([]T, error) {
var collection = make([]T, 0)
return collection, service.Connection.ViewTx(func(tx portainer.Transaction) error {
var err error
collection, err = service.Tx(tx).ReadAll()
return err
})
}
func (service BaseDataService[T, I]) Update(ID I, element *T) error {
return service.Connection.UpdateTx(func(tx portainer.Transaction) error {
return service.Tx(tx).Update(ID, element)
})
}
func (service BaseDataService[T, I]) Delete(ID I) error {
return service.Connection.UpdateTx(func(tx portainer.Transaction) error {
return service.Tx(tx).Delete(ID)
})
}

View File

@@ -0,0 +1,49 @@
package dataservices
import (
portainer "github.com/portainer/portainer/api"
"golang.org/x/exp/constraints"
)
type BaseDataServiceTx[T any, I constraints.Integer] struct {
Bucket string
Connection portainer.Connection
Tx portainer.Transaction
}
func (service BaseDataServiceTx[T, I]) BucketName() string {
return service.Bucket
}
func (service BaseDataServiceTx[T, I]) Read(ID I) (*T, error) {
var element T
identifier := service.Connection.ConvertToKey(int(ID))
err := service.Tx.GetObject(service.Bucket, identifier, &element)
if err != nil {
return nil, err
}
return &element, nil
}
func (service BaseDataServiceTx[T, I]) ReadAll() ([]T, error) {
var collection = make([]T, 0)
return collection, service.Tx.GetAll(
service.Bucket,
new(T),
AppendFn(&collection),
)
}
func (service BaseDataServiceTx[T, I]) Update(ID I, element *T) error {
identifier := service.Connection.ConvertToKey(int(ID))
return service.Tx.UpdateObject(service.Bucket, identifier, element)
}
func (service BaseDataServiceTx[T, I]) Delete(ID I) error {
identifier := service.Connection.ConvertToKey(int(ID))
return service.Tx.DeleteObject(service.Bucket, identifier)
}

View File

@@ -1,24 +1,16 @@
package customtemplate
import (
"fmt"
portainer "github.com/portainer/portainer/api"
"github.com/sirupsen/logrus"
"github.com/portainer/portainer/api/dataservices"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "customtemplates"
)
// BucketName represents the name of the bucket where this service stores data.
const BucketName = "customtemplates"
// Service represents a service for managing custom template data.
type Service struct {
connection portainer.Connection
}
func (service *Service) BucketName() string {
return BucketName
dataservices.BaseDataService[portainer.CustomTemplate, portainer.CustomTemplateID]
}
// NewService creates a new instance of a service.
@@ -29,63 +21,20 @@ func NewService(connection portainer.Connection) (*Service, error) {
}
return &Service{
connection: connection,
BaseDataService: dataservices.BaseDataService[portainer.CustomTemplate, portainer.CustomTemplateID]{
Bucket: BucketName,
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.GetAll(
BucketName,
&portainer.CustomTemplate{},
func(obj interface{}) (interface{}, error) {
//var tag portainer.Tag
customTemplate, ok := obj.(*portainer.CustomTemplate)
if !ok {
logrus.WithField("obj", obj).Errorf("Failed to convert to CustomTemplate object")
return nil, fmt.Errorf("Failed to convert to CustomTemplate object: %s", obj)
}
customTemplates = append(customTemplates, *customTemplate)
return &portainer.CustomTemplate{}, 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 := service.connection.ConvertToKey(int(ID))
err := service.connection.GetObject(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 := service.connection.ConvertToKey(int(ID))
return service.connection.UpdateObject(BucketName, identifier, customTemplate)
}
// DeleteCustomTemplate deletes an custom template.
func (service *Service) DeleteCustomTemplate(ID portainer.CustomTemplateID) error {
identifier := service.connection.ConvertToKey(int(ID))
return service.connection.DeleteObject(BucketName, identifier)
}
// CreateCustomTemplate uses the existing id and saves it.
// TODO: where does the ID come from, and is it safe?
func (service *Service) Create(customTemplate *portainer.CustomTemplate) error {
return service.connection.CreateObjectWithId(BucketName, int(customTemplate.ID), customTemplate)
return service.Connection.CreateObjectWithId(BucketName, int(customTemplate.ID), customTemplate)
}
// GetNextIdentifier returns the next identifier for a custom template.
func (service *Service) GetNextIdentifier() int {
return service.connection.GetNextIdentifier(BucketName)
return service.Connection.GetNextIdentifier(BucketName)
}

View File

@@ -1,20 +1,16 @@
package edgegroup
import (
"fmt"
portainer "github.com/portainer/portainer/api"
"github.com/sirupsen/logrus"
"github.com/portainer/portainer/api/dataservices"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "edgegroups"
)
// BucketName represents the name of the bucket where this service stores data.
const BucketName = "edgegroups"
// Service represents a service for managing Edge group data.
type Service struct {
connection portainer.Connection
dataservices.BaseDataService[portainer.EdgeGroup, portainer.EdgeGroupID]
}
func (service *Service) BucketName() string {
@@ -29,62 +25,36 @@ func NewService(connection portainer.Connection) (*Service, error) {
}
return &Service{
connection: connection,
BaseDataService: dataservices.BaseDataService[portainer.EdgeGroup, portainer.EdgeGroupID]{
Bucket: BucketName,
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.GetAllWithJsoniter(
BucketName,
&portainer.EdgeGroup{},
func(obj interface{}) (interface{}, error) {
group, ok := obj.(*portainer.EdgeGroup)
if !ok {
logrus.WithField("obj", obj).Errorf("Failed to convert to EdgeGroup object")
return nil, fmt.Errorf("Failed to convert to EdgeGroup object: %s", obj)
}
groups = append(groups, *group)
return &portainer.EdgeGroup{}, 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 := service.connection.ConvertToKey(int(ID))
err := service.connection.GetObject(BucketName, identifier, &group)
if err != nil {
return nil, err
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
return ServiceTx{
BaseDataServiceTx: dataservices.BaseDataServiceTx[portainer.EdgeGroup, portainer.EdgeGroupID]{
Bucket: BucketName,
Connection: service.Connection,
Tx: tx,
},
}
return &group, nil
}
// UpdateEdgeGroup updates an Edge group.
func (service *Service) UpdateEdgeGroup(ID portainer.EdgeGroupID, group *portainer.EdgeGroup) error {
identifier := service.connection.ConvertToKey(int(ID))
return service.connection.UpdateObject(BucketName, identifier, group)
}
// Deprecated: UpdateEdgeGroupFunc updates an edge group inside a transaction avoiding data races.
func (service *Service) UpdateEdgeGroupFunc(ID portainer.EdgeGroupID, updateFunc func(edgeGroup *portainer.EdgeGroup)) error {
id := service.Connection.ConvertToKey(int(ID))
edgeGroup := &portainer.EdgeGroup{}
// DeleteEdgeGroup deletes an Edge group.
func (service *Service) DeleteEdgeGroup(ID portainer.EdgeGroupID) error {
identifier := service.connection.ConvertToKey(int(ID))
return service.connection.DeleteObject(BucketName, identifier)
return service.Connection.UpdateObjectFunc(BucketName, id, edgeGroup, func() {
updateFunc(edgeGroup)
})
}
// CreateEdgeGroup assign an ID to a new Edge group and saves it.
func (service *Service) Create(group *portainer.EdgeGroup) error {
return service.connection.CreateObject(
BucketName,
func(id uint64) (int, interface{}) {
group.ID = portainer.EdgeGroupID(id)
return int(group.ID), group
},
)
return service.Connection.UpdateTx(func(tx portainer.Transaction) error {
return service.Tx(tx).Create(group)
})
}

View File

@@ -0,0 +1,27 @@
package edgegroup
import (
"errors"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
)
type ServiceTx struct {
dataservices.BaseDataServiceTx[portainer.EdgeGroup, portainer.EdgeGroupID]
}
// UpdateEdgeGroupFunc is a no-op inside a transaction.
func (service ServiceTx) UpdateEdgeGroupFunc(ID portainer.EdgeGroupID, updateFunc func(edgeGroup *portainer.EdgeGroup)) error {
return errors.New("cannot be called inside a transaction")
}
func (service ServiceTx) Create(group *portainer.EdgeGroup) error {
return service.Tx.CreateObject(
BucketName,
func(id uint64) (int, any) {
group.ID = portainer.EdgeGroupID(id)
return int(group.ID), group
},
)
}

View File

@@ -1,24 +1,16 @@
package edgejob
import (
"fmt"
portainer "github.com/portainer/portainer/api"
"github.com/sirupsen/logrus"
"github.com/portainer/portainer/api/dataservices"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "edgejobs"
)
// BucketName represents the name of the bucket where this service stores data.
const BucketName = "edgejobs"
// Service represents a service for managing edge jobs data.
type Service struct {
connection portainer.Connection
}
func (service *Service) BucketName() string {
return BucketName
dataservices.BaseDataService[portainer.EdgeJob, portainer.EdgeJobID]
}
// NewService creates a new instance of a service.
@@ -29,68 +21,50 @@ func NewService(connection portainer.Connection) (*Service, error) {
}
return &Service{
connection: connection,
BaseDataService: dataservices.BaseDataService[portainer.EdgeJob, portainer.EdgeJobID]{
Bucket: BucketName,
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.GetAll(
BucketName,
&portainer.EdgeJob{},
func(obj interface{}) (interface{}, error) {
//var tag portainer.Tag
job, ok := obj.(*portainer.EdgeJob)
if !ok {
logrus.WithField("obj", obj).Errorf("Failed to convert to EdgeJob object")
return nil, fmt.Errorf("Failed to convert to EdgeJob object: %s", obj)
}
edgeJobs = append(edgeJobs, *job)
return &portainer.EdgeJob{}, 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 := service.connection.ConvertToKey(int(ID))
err := service.connection.GetObject(BucketName, identifier, &edgeJob)
if err != nil {
return nil, err
}
return &edgeJob, nil
}
// CreateEdgeJob creates a new Edge job
func (service *Service) Create(edgeJob *portainer.EdgeJob) error {
return service.connection.CreateObject(
BucketName,
func(id uint64) (int, interface{}) {
edgeJob.ID = portainer.EdgeJobID(id)
return int(edgeJob.ID), edgeJob
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
return ServiceTx{
BaseDataServiceTx: dataservices.BaseDataServiceTx[portainer.EdgeJob, portainer.EdgeJobID]{
Bucket: BucketName,
Connection: service.Connection,
Tx: tx,
},
}
}
// Create creates a new EdgeJob
func (service *Service) Create(edgeJob *portainer.EdgeJob) error {
return service.CreateWithID(portainer.EdgeJobID(service.GetNextIdentifier()), edgeJob)
}
// CreateWithID creates a new EdgeJob
func (service *Service) CreateWithID(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error {
edgeJob.ID = ID
return service.Connection.CreateObjectWithId(
BucketName,
int(edgeJob.ID),
edgeJob,
)
}
// UpdateEdgeJob updates an Edge job by ID
func (service *Service) UpdateEdgeJob(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error {
identifier := service.connection.ConvertToKey(int(ID))
return service.connection.UpdateObject(BucketName, identifier, edgeJob)
}
// UpdateEdgeJobFunc updates an edge job inside a transaction avoiding data races.
func (service *Service) UpdateEdgeJobFunc(ID portainer.EdgeJobID, updateFunc func(edgeJob *portainer.EdgeJob)) error {
id := service.Connection.ConvertToKey(int(ID))
edgeJob := &portainer.EdgeJob{}
// DeleteEdgeJob deletes an Edge job
func (service *Service) DeleteEdgeJob(ID portainer.EdgeJobID) error {
identifier := service.connection.ConvertToKey(int(ID))
return service.connection.DeleteObject(BucketName, identifier)
return service.Connection.UpdateObjectFunc(BucketName, id, edgeJob, func() {
updateFunc(edgeJob)
})
}
// GetNextIdentifier returns the next identifier for an environment(endpoint).
func (service *Service) GetNextIdentifier() int {
return service.connection.GetNextIdentifier(BucketName)
return service.Connection.GetNextIdentifier(BucketName)
}

View File

@@ -0,0 +1,34 @@
package edgejob
import (
"errors"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
)
type ServiceTx struct {
dataservices.BaseDataServiceTx[portainer.EdgeJob, portainer.EdgeJobID]
}
// Create creates a new EdgeJob
func (service ServiceTx) Create(edgeJob *portainer.EdgeJob) error {
return service.CreateWithID(portainer.EdgeJobID(service.GetNextIdentifier()), edgeJob)
}
// CreateWithID creates a new EdgeJob
func (service ServiceTx) CreateWithID(ID portainer.EdgeJobID, edgeJob *portainer.EdgeJob) error {
edgeJob.ID = ID
return service.Tx.CreateObjectWithId(BucketName, int(edgeJob.ID), edgeJob)
}
// UpdateEdgeJobFunc is a no-op inside a transaction.
func (service ServiceTx) UpdateEdgeJobFunc(ID portainer.EdgeJobID, updateFunc func(edgeJob *portainer.EdgeJob)) error {
return errors.New("cannot be called inside a transaction")
}
// GetNextIdentifier returns the next identifier for an environment(endpoint).
func (service ServiceTx) GetNextIdentifier() int {
return service.Tx.GetNextIdentifier(BucketName)
}

View File

@@ -1,20 +1,21 @@
package edgestack
import (
"fmt"
"sync"
portainer "github.com/portainer/portainer/api"
"github.com/sirupsen/logrus"
"github.com/portainer/portainer/api/dataservices"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "edge_stack"
)
// BucketName represents the name of the bucket where this service stores data.
const BucketName = "edge_stack"
// Service represents a service for managing Edge stack data.
type Service struct {
connection portainer.Connection
connection portainer.Connection
idxVersion map[portainer.EdgeStackID]int
mu sync.RWMutex
cacheInvalidationFn func(portainer.EdgeStackID)
}
func (service *Service) BucketName() string {
@@ -22,36 +23,50 @@ func (service *Service) BucketName() string {
}
// NewService creates a new instance of a service.
func NewService(connection portainer.Connection) (*Service, error) {
func NewService(connection portainer.Connection, cacheInvalidationFn func(portainer.EdgeStackID)) (*Service, error) {
err := connection.SetServiceName(BucketName)
if err != nil {
return nil, err
}
return &Service{
connection: connection,
}, nil
s := &Service{
connection: connection,
idxVersion: make(map[portainer.EdgeStackID]int),
cacheInvalidationFn: cacheInvalidationFn,
}
if s.cacheInvalidationFn == nil {
s.cacheInvalidationFn = func(portainer.EdgeStackID) {}
}
es, err := s.EdgeStacks()
if err != nil {
return nil, err
}
for _, e := range es {
s.idxVersion[e.ID] = e.Version
}
return s, nil
}
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
return ServiceTx{
service: service,
tx: tx,
}
}
// EdgeStacks returns an array containing all edge stacks
func (service *Service) EdgeStacks() ([]portainer.EdgeStack, error) {
var stacks = make([]portainer.EdgeStack, 0)
err := service.connection.GetAll(
return stacks, service.connection.GetAll(
BucketName,
&portainer.EdgeStack{},
func(obj interface{}) (interface{}, error) {
//var tag portainer.Tag
stack, ok := obj.(*portainer.EdgeStack)
if !ok {
logrus.WithField("obj", obj).Errorf("Failed to convert to EdgeStack object")
return nil, fmt.Errorf("Failed to convert to EdgeStack object: %s", obj)
}
stacks = append(stacks, *stack)
return &portainer.EdgeStack{}, nil
})
return stacks, err
dataservices.AppendFn(&stacks),
)
}
// EdgeStack returns an Edge stack by ID.
@@ -67,28 +82,92 @@ func (service *Service) EdgeStack(ID portainer.EdgeStackID) (*portainer.EdgeStac
return &stack, nil
}
// EdgeStackVersion returns the version of the given edge stack ID directly from an in-memory index
func (service *Service) EdgeStackVersion(ID portainer.EdgeStackID) (int, bool) {
service.mu.RLock()
v, ok := service.idxVersion[ID]
service.mu.RUnlock()
return v, ok
}
// CreateEdgeStack saves an Edge stack object to db.
func (service *Service) Create(id portainer.EdgeStackID, edgeStack *portainer.EdgeStack) error {
edgeStack.ID = id
return service.connection.CreateObjectWithId(
err := service.connection.CreateObjectWithId(
BucketName,
int(edgeStack.ID),
edgeStack,
)
if err != nil {
return err
}
service.mu.Lock()
service.idxVersion[id] = edgeStack.Version
service.cacheInvalidationFn(id)
service.mu.Unlock()
return nil
}
// UpdateEdgeStack updates an Edge stack.
// Deprecated: Use UpdateEdgeStackFunc instead.
func (service *Service) UpdateEdgeStack(ID portainer.EdgeStackID, edgeStack *portainer.EdgeStack) error {
service.mu.Lock()
defer service.mu.Unlock()
identifier := service.connection.ConvertToKey(int(ID))
return service.connection.UpdateObject(BucketName, identifier, edgeStack)
err := service.connection.UpdateObject(BucketName, identifier, edgeStack)
if err != nil {
return err
}
service.idxVersion[ID] = edgeStack.Version
service.cacheInvalidationFn(ID)
return nil
}
// UpdateEdgeStackFunc updates an Edge stack inside a transaction avoiding data races.
func (service *Service) UpdateEdgeStackFunc(ID portainer.EdgeStackID, updateFunc func(edgeStack *portainer.EdgeStack)) error {
id := service.connection.ConvertToKey(int(ID))
edgeStack := &portainer.EdgeStack{}
service.mu.Lock()
defer service.mu.Unlock()
return service.connection.UpdateObjectFunc(BucketName, id, edgeStack, func() {
updateFunc(edgeStack)
service.idxVersion[ID] = edgeStack.Version
service.cacheInvalidationFn(ID)
})
}
// UpdateEdgeStackFuncTx is a helper function used to call UpdateEdgeStackFunc inside a transaction.
func (service *Service) UpdateEdgeStackFuncTx(tx portainer.Transaction, ID portainer.EdgeStackID, updateFunc func(edgeStack *portainer.EdgeStack)) error {
return service.Tx(tx).UpdateEdgeStackFunc(ID, updateFunc)
}
// DeleteEdgeStack deletes an Edge stack.
func (service *Service) DeleteEdgeStack(ID portainer.EdgeStackID) error {
service.mu.Lock()
defer service.mu.Unlock()
identifier := service.connection.ConvertToKey(int(ID))
return service.connection.DeleteObject(BucketName, identifier)
err := service.connection.DeleteObject(BucketName, identifier)
if err != nil {
return err
}
delete(service.idxVersion, ID)
service.cacheInvalidationFn(ID)
return nil
}
// GetNextIdentifier returns the next identifier for an environment(endpoint).

View File

@@ -0,0 +1,137 @@
package edgestack
import (
"fmt"
portainer "github.com/portainer/portainer/api"
"github.com/rs/zerolog/log"
)
type ServiceTx struct {
service *Service
tx portainer.Transaction
}
func (service ServiceTx) BucketName() string {
return BucketName
}
// EdgeStacks returns an array containing all edge stacks
func (service ServiceTx) EdgeStacks() ([]portainer.EdgeStack, error) {
var stacks = make([]portainer.EdgeStack, 0)
err := service.tx.GetAll(
BucketName,
&portainer.EdgeStack{},
func(obj any) (any, error) {
stack, ok := obj.(*portainer.EdgeStack)
if !ok {
log.Debug().Str("obj", fmt.Sprintf("%#v", obj)).Msg("failed to convert to EdgeStack object")
return nil, fmt.Errorf("failed to convert to EdgeStack object: %s", obj)
}
stacks = append(stacks, *stack)
return &portainer.EdgeStack{}, nil
})
return stacks, err
}
// EdgeStack returns an Edge stack by ID.
func (service ServiceTx) EdgeStack(ID portainer.EdgeStackID) (*portainer.EdgeStack, error) {
var stack portainer.EdgeStack
identifier := service.service.connection.ConvertToKey(int(ID))
err := service.tx.GetObject(BucketName, identifier, &stack)
if err != nil {
return nil, err
}
return &stack, nil
}
// EdgeStackVersion returns the version of the given edge stack ID directly from an in-memory index
func (service ServiceTx) EdgeStackVersion(ID portainer.EdgeStackID) (int, bool) {
service.service.mu.RLock()
v, ok := service.service.idxVersion[ID]
service.service.mu.RUnlock()
return v, ok
}
// CreateEdgeStack saves an Edge stack object to db.
func (service ServiceTx) Create(id portainer.EdgeStackID, edgeStack *portainer.EdgeStack) error {
edgeStack.ID = id
err := service.tx.CreateObjectWithId(
BucketName,
int(edgeStack.ID),
edgeStack,
)
if err != nil {
return err
}
service.service.mu.Lock()
service.service.idxVersion[id] = edgeStack.Version
service.service.cacheInvalidationFn(id)
service.service.mu.Unlock()
return nil
}
// UpdateEdgeStack updates an Edge stack.
func (service ServiceTx) UpdateEdgeStack(ID portainer.EdgeStackID, edgeStack *portainer.EdgeStack) error {
service.service.mu.Lock()
defer service.service.mu.Unlock()
identifier := service.service.connection.ConvertToKey(int(ID))
err := service.tx.UpdateObject(BucketName, identifier, edgeStack)
if err != nil {
return err
}
service.service.idxVersion[ID] = edgeStack.Version
service.service.cacheInvalidationFn(ID)
return nil
}
// Deprecated: use UpdateEdgeStack inside a transaction instead.
func (service ServiceTx) UpdateEdgeStackFunc(ID portainer.EdgeStackID, updateFunc func(edgeStack *portainer.EdgeStack)) error {
edgeStack, err := service.EdgeStack(ID)
if err != nil {
return err
}
updateFunc(edgeStack)
return service.UpdateEdgeStack(ID, edgeStack)
}
// DeleteEdgeStack deletes an Edge stack.
func (service ServiceTx) DeleteEdgeStack(ID portainer.EdgeStackID) error {
service.service.mu.Lock()
defer service.service.mu.Unlock()
identifier := service.service.connection.ConvertToKey(int(ID))
err := service.tx.DeleteObject(BucketName, identifier)
if err != nil {
return err
}
delete(service.service.idxVersion, ID)
service.service.cacheInvalidationFn(ID)
return nil
}
// GetNextIdentifier returns the next identifier for an environment(endpoint).
func (service ServiceTx) GetNextIdentifier() int {
return service.tx.GetNextIdentifier(BucketName)
}

View File

@@ -1,20 +1,24 @@
package endpoint
import (
"fmt"
"sync"
"time"
portainer "github.com/portainer/portainer/api"
"github.com/sirupsen/logrus"
"github.com/portainer/portainer/api/dataservices"
"github.com/rs/zerolog/log"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "endpoints"
)
// BucketName represents the name of the bucket where this service stores data.
const BucketName = "endpoints"
// Service represents a service for managing environment(endpoint) data.
type Service struct {
connection portainer.Connection
mu sync.RWMutex
idxEdgeID map[string]portainer.EndpointID
heartbeats sync.Map
}
func (service *Service) BucketName() string {
@@ -28,62 +32,150 @@ func NewService(connection portainer.Connection) (*Service, error) {
return nil, err
}
return &Service{
s := &Service{
connection: connection,
}, nil
}
idxEdgeID: make(map[string]portainer.EndpointID),
}
// Endpoint returns an environment(endpoint) by ID.
func (service *Service) Endpoint(ID portainer.EndpointID) (*portainer.Endpoint, error) {
var endpoint portainer.Endpoint
identifier := service.connection.ConvertToKey(int(ID))
err := service.connection.GetObject(BucketName, identifier, &endpoint)
es, err := s.endpoints()
if err != nil {
return nil, err
}
return &endpoint, nil
for _, e := range es {
if len(e.EdgeID) > 0 {
s.idxEdgeID[e.EdgeID] = e.ID
}
s.heartbeats.Store(e.ID, e.LastCheckInDate)
}
return s, nil
}
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
return ServiceTx{
service: service,
tx: tx,
}
}
// Endpoint returns an environment(endpoint) by ID.
func (service *Service) Endpoint(ID portainer.EndpointID) (*portainer.Endpoint, error) {
var endpoint *portainer.Endpoint
var err error
err = service.connection.ViewTx(func(tx portainer.Transaction) error {
endpoint, err = service.Tx(tx).Endpoint(ID)
return err
})
if err != nil {
return nil, err
}
endpoint.LastCheckInDate, _ = service.Heartbeat(ID)
return endpoint, nil
}
// UpdateEndpoint updates an environment(endpoint).
func (service *Service) UpdateEndpoint(ID portainer.EndpointID, endpoint *portainer.Endpoint) error {
identifier := service.connection.ConvertToKey(int(ID))
return service.connection.UpdateObject(BucketName, identifier, endpoint)
return service.connection.UpdateTx(func(tx portainer.Transaction) error {
return service.Tx(tx).UpdateEndpoint(ID, endpoint)
})
}
// DeleteEndpoint deletes an environment(endpoint).
func (service *Service) DeleteEndpoint(ID portainer.EndpointID) error {
identifier := service.connection.ConvertToKey(int(ID))
return service.connection.DeleteObject(BucketName, identifier)
return service.connection.UpdateTx(func(tx portainer.Transaction) error {
return service.Tx(tx).DeleteEndpoint(ID)
})
}
// Endpoints return an array containing all the environments(endpoints).
func (service *Service) Endpoints() ([]portainer.Endpoint, error) {
var endpoints = make([]portainer.Endpoint, 0)
func (service *Service) endpoints() ([]portainer.Endpoint, error) {
var endpoints []portainer.Endpoint
var err error
err := service.connection.GetAllWithJsoniter(
BucketName,
&portainer.Endpoint{},
func(obj interface{}) (interface{}, error) {
endpoint, ok := obj.(*portainer.Endpoint)
if !ok {
logrus.WithField("obj", obj).Errorf("Failed to convert to Endpoint object")
return nil, fmt.Errorf("failed to convert to Endpoint object: %s", obj)
}
endpoints = append(endpoints, *endpoint)
return &portainer.Endpoint{}, nil
})
err = service.connection.ViewTx(func(tx portainer.Transaction) error {
endpoints, err = service.Tx(tx).Endpoints()
return err
})
return endpoints, err
}
// Endpoints return an array containing all the environments(endpoints).
func (service *Service) Endpoints() ([]portainer.Endpoint, error) {
endpoints, err := service.endpoints()
if err != nil {
return nil, err
}
for i, e := range endpoints {
t, _ := service.Heartbeat(e.ID)
endpoints[i].LastCheckInDate = t
}
return endpoints, nil
}
// EndpointIDByEdgeID returns the EndpointID from the given EdgeID using an in-memory index
func (service *Service) EndpointIDByEdgeID(edgeID string) (portainer.EndpointID, bool) {
service.mu.RLock()
endpointID, ok := service.idxEdgeID[edgeID]
service.mu.RUnlock()
return endpointID, ok
}
func (service *Service) Heartbeat(endpointID portainer.EndpointID) (int64, bool) {
if t, ok := service.heartbeats.Load(endpointID); ok {
return t.(int64), true
}
return 0, false
}
func (service *Service) UpdateHeartbeat(endpointID portainer.EndpointID) {
service.heartbeats.Store(endpointID, time.Now().Unix())
}
// CreateEndpoint assign an ID to a new environment(endpoint) and saves it.
func (service *Service) Create(endpoint *portainer.Endpoint) error {
return service.connection.CreateObjectWithSetSequence(BucketName, int(endpoint.ID), endpoint)
return service.connection.UpdateTx(func(tx portainer.Transaction) error {
return service.Tx(tx).Create(endpoint)
})
}
func (service *Service) EndpointsByTeamID(teamID portainer.TeamID) ([]portainer.Endpoint, error) {
var endpoints = make([]portainer.Endpoint, 0)
return endpoints, service.connection.GetAll(
BucketName,
&portainer.Endpoint{},
dataservices.FilterFn(&endpoints, func(e portainer.Endpoint) bool {
for t := range e.TeamAccessPolicies {
if t == teamID {
return true
}
}
return false
}),
)
}
// GetNextIdentifier returns the next identifier for an environment(endpoint).
func (service *Service) GetNextIdentifier() int {
return service.connection.GetNextIdentifier(BucketName)
var identifier int
if err := service.connection.UpdateTx(func(tx portainer.Transaction) error {
identifier = service.Tx(tx).GetNextIdentifier()
return nil
}); err != nil {
log.Error().Err(err).Str("bucket", BucketName).Msg("could not get the next identifier")
}
return identifier
}

View File

@@ -0,0 +1,146 @@
package endpoint
import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/edge/cache"
"github.com/rs/zerolog/log"
)
type ServiceTx struct {
service *Service
tx portainer.Transaction
}
func (service ServiceTx) BucketName() string {
return BucketName
}
// Endpoint returns an environment(endpoint) by ID.
func (service ServiceTx) Endpoint(ID portainer.EndpointID) (*portainer.Endpoint, error) {
var endpoint portainer.Endpoint
identifier := service.service.connection.ConvertToKey(int(ID))
if err := service.tx.GetObject(BucketName, identifier, &endpoint); err != nil {
return nil, err
}
endpoint.LastCheckInDate, _ = service.service.Heartbeat(ID)
return &endpoint, nil
}
// UpdateEndpoint updates an environment(endpoint).
func (service ServiceTx) UpdateEndpoint(ID portainer.EndpointID, endpoint *portainer.Endpoint) error {
identifier := service.service.connection.ConvertToKey(int(ID))
if err := service.tx.UpdateObject(BucketName, identifier, endpoint); err != nil {
return err
}
service.service.mu.Lock()
if len(endpoint.EdgeID) > 0 {
service.service.idxEdgeID[endpoint.EdgeID] = ID
}
service.service.heartbeats.Store(ID, endpoint.LastCheckInDate)
service.service.mu.Unlock()
cache.Del(endpoint.ID)
return nil
}
// DeleteEndpoint deletes an environment(endpoint).
func (service ServiceTx) DeleteEndpoint(ID portainer.EndpointID) error {
identifier := service.service.connection.ConvertToKey(int(ID))
if err := service.tx.DeleteObject(BucketName, identifier); err != nil {
return err
}
service.service.mu.Lock()
for edgeID, endpointID := range service.service.idxEdgeID {
if endpointID == ID {
delete(service.service.idxEdgeID, edgeID)
break
}
}
service.service.heartbeats.Delete(ID)
service.service.mu.Unlock()
cache.Del(ID)
return nil
}
// Endpoints return an array containing all the environments(endpoints).
func (service ServiceTx) Endpoints() ([]portainer.Endpoint, error) {
var endpoints = make([]portainer.Endpoint, 0)
return endpoints, service.tx.GetAll(
BucketName,
&portainer.Endpoint{},
dataservices.AppendFn(&endpoints),
)
}
func (service ServiceTx) EndpointIDByEdgeID(edgeID string) (portainer.EndpointID, bool) {
log.Error().Str("func", "EndpointIDByEdgeID").Msg("cannot be called inside a transaction")
return 0, false
}
func (service ServiceTx) Heartbeat(endpointID portainer.EndpointID) (int64, bool) {
log.Error().Str("func", "Heartbeat").Msg("cannot be called inside a transaction")
return 0, false
}
func (service ServiceTx) UpdateHeartbeat(endpointID portainer.EndpointID) {
log.Error().Str("func", "UpdateHeartbeat").Msg("cannot be called inside a transaction")
}
// CreateEndpoint assign an ID to a new environment(endpoint) and saves it.
func (service ServiceTx) Create(endpoint *portainer.Endpoint) error {
if err := service.tx.CreateObjectWithId(BucketName, int(endpoint.ID), endpoint); err != nil {
return err
}
service.service.mu.Lock()
if len(endpoint.EdgeID) > 0 {
service.service.idxEdgeID[endpoint.EdgeID] = endpoint.ID
}
service.service.heartbeats.Store(endpoint.ID, endpoint.LastCheckInDate)
service.service.mu.Unlock()
return nil
}
func (service ServiceTx) EndpointsByTeamID(teamID portainer.TeamID) ([]portainer.Endpoint, error) {
var endpoints = make([]portainer.Endpoint, 0)
return endpoints, service.tx.GetAll(
BucketName,
&portainer.Endpoint{},
dataservices.FilterFn(&endpoints, func(e portainer.Endpoint) bool {
for t := range e.TeamAccessPolicies {
if t == teamID {
return true
}
}
return false
}),
)
}
// GetNextIdentifier returns the next identifier for an environment(endpoint).
func (service ServiceTx) GetNextIdentifier() int {
return service.tx.GetNextIdentifier(BucketName)
}

View File

@@ -1,24 +1,15 @@
package endpointgroup
import (
"fmt"
portainer "github.com/portainer/portainer/api"
"github.com/sirupsen/logrus"
"github.com/portainer/portainer/api/dataservices"
)
const (
// BucketName represents the name of the bucket where this service stores data.
BucketName = "endpoint_groups"
)
const BucketName = "endpoint_groups"
// Service represents a service for managing environment(endpoint) data.
type Service struct {
connection portainer.Connection
}
func (service *Service) BucketName() string {
return BucketName
dataservices.BaseDataService[portainer.EndpointGroup, portainer.EndpointGroupID]
}
// NewService creates a new instance of a service.
@@ -29,61 +20,28 @@ func NewService(connection portainer.Connection) (*Service, error) {
}
return &Service{
connection: connection,
BaseDataService: dataservices.BaseDataService[portainer.EndpointGroup, portainer.EndpointGroupID]{
Bucket: BucketName,
Connection: connection,
},
}, nil
}
// EndpointGroup returns an environment(endpoint) group by ID.
func (service *Service) EndpointGroup(ID portainer.EndpointGroupID) (*portainer.EndpointGroup, error) {
var endpointGroup portainer.EndpointGroup
identifier := service.connection.ConvertToKey(int(ID))
err := service.connection.GetObject(BucketName, identifier, &endpointGroup)
if err != nil {
return nil, err
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
return ServiceTx{
BaseDataServiceTx: dataservices.BaseDataServiceTx[portainer.EndpointGroup, portainer.EndpointGroupID]{
Bucket: BucketName,
Connection: service.Connection,
Tx: tx,
},
}
return &endpointGroup, nil
}
// UpdateEndpointGroup updates an environment(endpoint) group.
func (service *Service) UpdateEndpointGroup(ID portainer.EndpointGroupID, endpointGroup *portainer.EndpointGroup) error {
identifier := service.connection.ConvertToKey(int(ID))
return service.connection.UpdateObject(BucketName, identifier, endpointGroup)
}
// DeleteEndpointGroup deletes an environment(endpoint) group.
func (service *Service) DeleteEndpointGroup(ID portainer.EndpointGroupID) error {
identifier := service.connection.ConvertToKey(int(ID))
return service.connection.DeleteObject(BucketName, identifier)
}
// EndpointGroups return an array containing all the environment(endpoint) groups.
func (service *Service) EndpointGroups() ([]portainer.EndpointGroup, error) {
var endpointGroups = make([]portainer.EndpointGroup, 0)
err := service.connection.GetAll(
BucketName,
&portainer.EndpointGroup{},
func(obj interface{}) (interface{}, error) {
//var tag portainer.Tag
endpointGroup, ok := obj.(*portainer.EndpointGroup)
if !ok {
logrus.WithField("obj", obj).Errorf("Failed to convert to EndpointGroup object")
return nil, fmt.Errorf("Failed to convert to EndpointGroup object: %s", obj)
}
endpointGroups = append(endpointGroups, *endpointGroup)
return &portainer.EndpointGroup{}, nil
})
return endpointGroups, err
}
// CreateEndpointGroup assign an ID to a new environment(endpoint) group and saves it.
func (service *Service) Create(endpointGroup *portainer.EndpointGroup) error {
return service.connection.CreateObject(
return service.Connection.CreateObject(
BucketName,
func(id uint64) (int, interface{}) {
func(id uint64) (int, any) {
endpointGroup.ID = portainer.EndpointGroupID(id)
return int(endpointGroup.ID), endpointGroup
},

View File

@@ -0,0 +1,21 @@
package endpointgroup
import (
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
)
type ServiceTx struct {
dataservices.BaseDataServiceTx[portainer.EndpointGroup, portainer.EndpointGroupID]
}
// CreateEndpointGroup assign an ID to a new environment(endpoint) group and saves it.
func (service ServiceTx) Create(endpointGroup *portainer.EndpointGroup) error {
return service.Tx.CreateObject(
BucketName,
func(id uint64) (int, any) {
endpointGroup.ID = portainer.EndpointGroupID(id)
return int(endpointGroup.ID), endpointGroup
},
)
}

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