Compare commits

..

161 Commits

Author SHA1 Message Date
testa113
749a9d1a47 allow nested kube error that's thrown to bubble up 2023-10-17 01:49:26 +01:00
testa113
cb8174bd24 don't parse long patch responses 2023-10-17 01:48:34 +01:00
testa113
b4e7c5f3fe use portainer errors for mapped functions 2023-10-17 01:48:18 +01:00
testa113
de0ec03446 fix(kubeapi): fix ts api error handling [EE-5558] 2023-10-17 01:46:24 +01: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
1196 changed files with 24971 additions and 14248 deletions

View File

@@ -23,6 +23,8 @@ parserOptions:
modules: true
rules:
no-console: error
no-alert: error
no-control-regex: 'off'
no-empty: warn
no-empty-function: warn
@@ -86,8 +88,8 @@ overrides:
no-plusplus: off
func-style: [error, 'declaration']
import/prefer-default-export: off
no-use-before-define: "off"
'@typescript-eslint/no-use-before-define': ['error', { functions: false, "allowNamedExports": true }]
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

View File

@@ -93,6 +93,8 @@ body:
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.19.1'
- '2.19.0'
- '2.18.4'
- '2.18.3'
- '2.18.2'
@@ -102,8 +104,6 @@ body:
- '2.16.2'
- '2.16.1'
- '2.16.0'
- '2.15.1'
- '2.15.0'
validations:
required: true

View File

@@ -25,7 +25,7 @@ jobs:
cache: 'yarn'
- uses: actions/setup-go@v4
with:
go-version: 1.19.5
go-version: 1.21.0
- run: yarn --frozen-lockfile
- name: Run linters
uses: wearerequired/lint-action@v1
@@ -41,6 +41,6 @@ jobs:
- name: GolangCI-Lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.52.2
version: v1.54.1
working-directory: api
args: --timeout=10m -c .golangci.yaml

View File

@@ -61,7 +61,7 @@ jobs:
- name: install Go
uses: actions/setup-go@v3
with:
go-version: '1.19.5'
go-version: '1.21.0'
- name: download Go modules
run: cd ./api && go get -t -v -d ./...
@@ -72,7 +72,7 @@ jobs:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run: |
yarn global add snyk
snyk test --file=./api/go.mod --json-file-output=snyk.json 2>/dev/null || :
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

View File

@@ -7,7 +7,7 @@ on:
- edited
paths:
- 'package.json'
- 'api/go.mod'
- 'go.mod'
- 'gruntfile.js'
- 'build/linux/Dockerfile'
- 'build/linux/alpine.Dockerfile'
@@ -84,7 +84,7 @@ jobs:
- name: install Go
uses: actions/setup-go@v3
with:
go-version: '1.19.5'
go-version: '1.21.0'
- name: download Go modules
run: cd ./api && go get -t -v -d ./...
@@ -95,7 +95,7 @@ jobs:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
run: |
yarn global add snyk
snyk test --file=./api/go.mod --json-file-output=snyk.json 2>/dev/null || :
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
@@ -143,10 +143,10 @@ jobs:
- name: checkout code
uses: actions/checkout@master
- name: install Go 1.19.5
- name: install Go 1.21.0
uses: actions/setup-go@v3
with:
go-version: '1.19.5'
go-version: '1.21.0'
- name: install Node.js 18.x
uses: actions/setup-node@v3

View File

@@ -13,13 +13,13 @@ jobs:
- run: yarn --frozen-lockfile
- name: Run tests
run: yarn jest --maxWorkers=2
run: make test-client ARGS="--maxWorkers=2"
test-server:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v3
with:
go-version: 1.19.5
go-version: 1.21.0
- name: Run tests
run: make test-server

View File

@@ -15,7 +15,7 @@ jobs:
- uses: actions/setup-go@v3
with:
go-version: '1.18'
go-version: '1.21.0'
- name: Download golang modules
run: cd ./api && go get -t -v -d ./...

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

@@ -65,7 +65,7 @@ clean: ## Remove all build and download artifacts
test: test-server test-client ## Run all tests
test-client: ## Run client tests
yarn test
yarn test $(ARGS)
test-server: ## Run server tests
cd api && $(GOTESTSUM) --format pkgname-and-test-fails --format-hide-empty-pkg --hide-summary skipped -- -cover ./...

View File

@@ -10,17 +10,21 @@ linters:
- exportloopref
linters-settings:
depguard:
list-type: denylist
include-go-root: true
packages:
- github.com/sirupsen/logrus
- golang.org/x/exp
packages-with-error-message:
- github.com/sirupsen/logrus: 'logging is allowed only by github.com/rs/zerolog'
ignore-file-rules:
- '**/*_test.go'
- '**/base.go'
- '**/base_tx.go'
rules:
main:
deny:
- 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

View File

@@ -7,9 +7,9 @@ import (
"sync"
"time"
httperror "github.com/portainer/libhttp/error"
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"
)

View File

@@ -30,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.

View File

@@ -75,10 +75,11 @@ func (service *Service) KeepTunnelAlive(endpointID portainer.EndpointID, ctx con
log.Debug().
Int("endpoint_id", int(endpointID)).
Float64("max_alive_minutes", maxAlive.Minutes()).
Msg("start")
Msg("KeepTunnelAlive: start")
maxAliveTicker := time.NewTicker(maxAlive)
defer maxAliveTicker.Stop()
pingTicker := time.NewTicker(tunnelCleanupInterval)
defer pingTicker.Stop()
@@ -91,13 +92,13 @@ func (service *Service) KeepTunnelAlive(endpointID portainer.EndpointID, ctx con
log.Debug().
Int("endpoint_id", int(endpointID)).
Err(err).
Msg("ping agent")
Msg("KeepTunnelAlive: ping agent")
}
case <-maxAliveTicker.C:
log.Debug().
Int("endpoint_id", int(endpointID)).
Float64("timeout_minutes", maxAlive.Minutes()).
Msg("tunnel keep alive timeout")
Msg("KeepTunnelAlive: tunnel keep alive timeout")
return
case <-ctx.Done():
@@ -105,7 +106,7 @@ func (service *Service) KeepTunnelAlive(endpointID portainer.EndpointID, ctx con
log.Debug().
Int("endpoint_id", int(endpointID)).
Err(err).
Msg("tunnel stop")
Msg("KeepTunnelAlive: tunnel stop")
return
}
@@ -126,8 +127,8 @@ func (service *Service) StartTunnelServer(addr, port string, snapshotService por
}
config := &chserver.Config{
Reverse: true,
PrivateKeyFile: privateKeyFile,
Reverse: true,
KeyFile: privateKeyFile,
}
chiselServer, err := chserver.NewServer(config)

View File

@@ -8,9 +8,9 @@ import (
"strings"
"time"
"github.com/portainer/libcrypto"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/internal/edge/cache"
"github.com/portainer/portainer/pkg/libcrypto"
"github.com/dchest/uniuri"
)

View File

@@ -39,9 +39,9 @@ func setLoggingMode(mode string) {
case "PRETTY":
log.Logger = log.Output(zerolog.ConsoleWriter{
Out: os.Stderr,
NoColor: true,
TimeFormat: "2006/01/02 03:04PM",
FormatMessage: formatMessage})
FormatMessage: formatMessage,
})
case "JSON":
log.Logger = log.Output(os.Stderr)
}
@@ -51,5 +51,6 @@ func formatMessage(i interface{}) string {
if i == nil {
return ""
}
return fmt.Sprintf("%s |", i)
}

View File

@@ -20,6 +20,7 @@ import (
"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/demo"
"github.com/portainer/portainer/api/docker"
dockerclient "github.com/portainer/portainer/api/docker/client"
@@ -42,6 +43,7 @@ import (
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/scheduler"
"github.com/portainer/portainer/api/stacks/deployments"
"github.com/portainer/portainer/pkg/featureflags"
@@ -119,11 +121,15 @@ func initDataStore(flags *portainer.CLIFlags, secretKey []byte, fileService port
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)
@@ -152,6 +158,16 @@ func initDataStore(flags *portainer.CLIFlags, secretKey []byte, fileService port
return store
}
// 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 {
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 {
@@ -248,11 +264,12 @@ func initSnapshotService(
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
}
@@ -383,6 +400,11 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
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 {
log.Fatal().Err(err).Msg("failed getting instance id")
@@ -434,15 +456,17 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
dockerClientFactory := initDockerClientFactory(digitalSignatureService, reverseTunnelService)
kubernetesClientFactory, err := initKubernetesClientFactory(digitalSignatureService, reverseTunnelService, dataStore, instanceID, *flags.AddrHTTPS, settings.UserSessionTimeout)
snapshotService, err := initSnapshotService(*flags.SnapshotInterval, dataStore, dockerClientFactory, kubernetesClientFactory, shutdownCtx)
authorizationService := authorization.NewService(dataStore)
authorizationService.K8sClientFactory = kubernetesClientFactory
pendingActionsService := pendingactions.NewService(dataStore, kubernetesClientFactory, authorizationService, shutdownCtx)
snapshotService, err := initSnapshotService(*flags.SnapshotInterval, dataStore, dockerClientFactory, kubernetesClientFactory, shutdownCtx, pendingActionsService)
if err != nil {
log.Fatal().Err(err).Msg("failed initializing snapshot service")
}
snapshotService.Start()
authorizationService := authorization.NewService(dataStore)
authorizationService.K8sClientFactory = kubernetesClientFactory
kubernetesTokenCacheManager := kubeproxy.NewTokenCacheManager()
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService(*flags.BaseURL, *flags.AddrHTTPS, sslSettings.CertPath)
@@ -602,6 +626,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
DemoService: demoService,
UpgradeService: upgradeService,
AdminCreationDone: adminCreationDone,
PendingActionsService: pendingActionsService,
}
}

View File

@@ -8,7 +8,7 @@ import (
"encoding/base64"
"encoding/hex"
"github.com/portainer/libcrypto"
"github.com/portainer/portainer/pkg/libcrypto"
)
const (

View File

@@ -35,6 +35,7 @@ type (
User() UserService
Version() VersionService
Webhook() WebhookService
PendingActions() PendingActionsService
}
DataStore interface {
@@ -72,6 +73,11 @@ type (
GetNextIdentifier() int
}
PendingActionsService interface {
BaseCRUD[portainer.PendingActions, portainer.PendingActionsID]
GetNextIdentifier() int
}
// EdgeStackService represents a service to manage Edge stacks
EdgeStackService interface {
EdgeStacks() ([]portainer.EdgeStack, error)

View File

@@ -0,0 +1,74 @@
package pendingactions
import (
"time"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
)
const (
BucketName = "pending_actions"
)
type Service struct {
dataservices.BaseDataService[portainer.PendingActions, portainer.PendingActionsID]
}
type ServiceTx struct {
dataservices.BaseDataServiceTx[portainer.PendingActions, portainer.PendingActionsID]
}
func NewService(connection portainer.Connection) (*Service, error) {
err := connection.SetServiceName(BucketName)
if err != nil {
return nil, err
}
return &Service{
BaseDataService: dataservices.BaseDataService[portainer.PendingActions, portainer.PendingActionsID]{
Bucket: BucketName,
Connection: connection,
},
}, nil
}
func (s Service) Create(config *portainer.PendingActions) error {
return s.Connection.UpdateTx(func(tx portainer.Transaction) error {
return s.Tx(tx).Create(config)
})
}
func (s Service) Update(ID portainer.PendingActionsID, config *portainer.PendingActions) error {
return s.Connection.UpdateTx(func(tx portainer.Transaction) error {
return s.Tx(tx).Update(ID, config)
})
}
func (service *Service) Tx(tx portainer.Transaction) ServiceTx {
return ServiceTx{
BaseDataServiceTx: dataservices.BaseDataServiceTx[portainer.PendingActions, portainer.PendingActionsID]{
Bucket: BucketName,
Connection: service.Connection,
Tx: tx,
},
}
}
func (s ServiceTx) Create(config *portainer.PendingActions) error {
return s.Tx.CreateObject(BucketName, func(id uint64) (int, interface{}) {
config.ID = portainer.PendingActionsID(id)
config.CreatedAt = time.Now().Unix()
return int(config.ID), config
})
}
func (s ServiceTx) Update(ID portainer.PendingActionsID, config *portainer.PendingActions) error {
return s.BaseDataServiceTx.Update(ID, config)
}
// GetNextIdentifier returns the next identifier for a custom template.
func (service *Service) GetNextIdentifier() int {
return service.Connection.GetNextIdentifier(BucketName)
}

View File

@@ -73,6 +73,10 @@ func (service *Service) IsUpdating() (bool, error) {
// StoreIsUpdating store the database updating status.
func (service *Service) StoreIsUpdating(isUpdating bool) error {
if isUpdating {
return service.connection.UpdateObject(BucketName, []byte(updatingKey), isUpdating)
}
return service.connection.DeleteObject(BucketName, []byte(updatingKey))
}

View File

@@ -50,10 +50,10 @@ func (store *Store) MigrateData() error {
if err != nil {
err = errors.Wrap(err, "failed to migrate database")
log.Warn().Msg("migration failed, restoring database to previous version")
err = store.restoreWithOptions(&BackupOptions{BackupPath: backupPath})
if err != nil {
return errors.Wrap(err, "failed to restore database")
log.Warn().Err(err).Msg("migration failed, restoring database to previous version")
restorErr := store.restoreWithOptions(&BackupOptions{BackupPath: backupPath})
if restorErr != nil {
return errors.Wrap(restorErr, "failed to restore database")
}
log.Info().Msg("database restored to previous version")

View File

@@ -1,7 +1,7 @@
package datastore
import (
portaineree "github.com/portainer/portainer/api"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/database/models"
"github.com/portainer/portainer/api/dataservices"
)
@@ -72,7 +72,7 @@ func dbVersionToSemanticVersion(dbVersion int) string {
func (store *Store) getOrMigrateLegacyVersion() (*models.Version, error) {
// Very old versions of portainer did not have a version bucket, lets set some defaults
dbVersion := 24
edition := int(portaineree.PortainerCE)
edition := int(portainer.PortainerCE)
instanceId := ""
// If we already have a version key, we don't need to migrate

View File

@@ -115,10 +115,16 @@ func (m *Migrator) updateEdgeStackStatusForDB100() error {
}
if environmentStatus.Details.Ok {
statusArray = append(statusArray, portainer.EdgeStackDeploymentStatus{
Type: portainer.EdgeStackStatusRunning,
Time: time.Now().Unix(),
})
statusArray = append(statusArray,
portainer.EdgeStackDeploymentStatus{
Type: portainer.EdgeStackStatusDeploymentReceived,
Time: time.Now().Unix(),
},
portainer.EdgeStackDeploymentStatus{
Type: portainer.EdgeStackStatusRunning,
Time: time.Now().Unix(),
},
)
}
if environmentStatus.Details.ImagesPulled {

View File

@@ -148,6 +148,17 @@ func (m *Migrator) LatestMigrations() Migrations {
return m.migrations[len(m.migrations)-1]
}
func (m *Migrator) GetMigratorCountOfCurrentAPIVersion() int {
migratorCount := 0
latestMigrations := m.LatestMigrations()
if latestMigrations.Version.Equal(semver.MustParse(portainer.APIVersion)) {
migratorCount = len(latestMigrations.MigrationFuncs)
}
return migratorCount
}
// !NOTE: Migration funtions should ideally be idempotent.
// ! Which simply means the function can run over the same data many times but only transform it once.
// ! In practice this really just means an extra check or two to ensure we're not destroying valid data.

View File

@@ -20,6 +20,7 @@ import (
"github.com/portainer/portainer/api/dataservices/extension"
"github.com/portainer/portainer/api/dataservices/fdoprofile"
"github.com/portainer/portainer/api/dataservices/helmuserrepository"
"github.com/portainer/portainer/api/dataservices/pendingactions"
"github.com/portainer/portainer/api/dataservices/registry"
"github.com/portainer/portainer/api/dataservices/resourcecontrol"
"github.com/portainer/portainer/api/dataservices/role"
@@ -72,6 +73,7 @@ type Store struct {
UserService *user.Service
VersionService *version.Service
WebhookService *webhook.Service
PendingActionsService *pendingactions.Service
}
func (store *Store) initServices() error {
@@ -238,9 +240,20 @@ func (store *Store) initServices() error {
}
store.ScheduleService = scheduleService
pendingActionsService, err := pendingactions.NewService(store.connection)
if err != nil {
return err
}
store.PendingActionsService = pendingActionsService
return nil
}
// PendingActions gives access to the PendingActions data management layer
func (store *Store) PendingActions() dataservices.PendingActionsService {
return store.PendingActionsService
}
// CustomTemplate gives access to the CustomTemplate data management layer
func (store *Store) CustomTemplate() dataservices.CustomTemplateService {
return store.CustomTemplateService

View File

@@ -16,6 +16,8 @@ func (tx *StoreTx) IsErrObjectNotFound(err error) bool {
func (tx *StoreTx) CustomTemplate() dataservices.CustomTemplateService { return nil }
func (tx *StoreTx) PendingActions() dataservices.PendingActionsService { return nil }
func (tx *StoreTx) EdgeGroup() dataservices.EdgeGroupService {
return tx.store.EdgeGroupService.Tx(tx.tx)
}

View File

@@ -602,6 +602,9 @@
"EnableTelemetry": true,
"EnforceEdgeID": false,
"FeatureFlagSettings": null,
"GlobalDeploymentOptions": {
"hideStacksFunctionality": false
},
"HelmRepositoryURL": "https://charts.bitnami.com/bitnami",
"InternalAuthSettings": {
"RequiredPasswordLength": 12

View File

@@ -2,6 +2,7 @@ package images
import (
"context"
"slices"
"strings"
"time"
@@ -9,7 +10,6 @@ import (
"github.com/docker/docker/api/types/filters"
portainer "github.com/portainer/portainer/api"
consts "github.com/portainer/portainer/api/docker/consts"
"github.com/portainer/portainer/api/internal/slices"
"github.com/opencontainers/go-digest"
"github.com/patrickmn/go-cache"

View File

@@ -3,48 +3,3 @@ package exec
import "regexp"
var stackNameNormalizeRegex = regexp.MustCompile("[^-_a-z0-9]+")
type StringSet map[string]bool
func NewStringSet() StringSet {
return make(StringSet)
}
func (s StringSet) Add(x string) {
s[x] = true
}
func (s StringSet) Remove(x string) {
if s.Contains(x) {
delete(s, x)
}
}
func (s StringSet) Contains(x string) bool {
_, ok := s[x]
return ok
}
func (s StringSet) Len() int {
return len(s)
}
func (s StringSet) List() []string {
list := make([]string, s.Len())
i := 0
for k := range s {
list[i] = k
i++
}
return list
}
func (s StringSet) Union(x StringSet) {
if x.Len() != 0 {
for k := range x {
s.Add(k)
}
}
}

View File

@@ -302,6 +302,38 @@ func (service *Service) UpdateStoreStackFileFromBytes(stackIdentifier, fileName
return service.wrapFileStore(stackStorePath), nil
}
// UpdateStoreStackFileFromBytesByVersion makes stack file backup and updates a new file from bytes.
// It returns the path to the folder where the file is stored.
func (service *Service) UpdateStoreStackFileFromBytesByVersion(stackIdentifier, fileName string, version int, commitHash string, data []byte) (string, error) {
stackStorePath := JoinPaths(ComposeStorePath, stackIdentifier)
versionStr := ""
if version != 0 {
versionStr = fmt.Sprintf("v%d", version)
}
if commitHash != "" {
versionStr = commitHash
}
if versionStr != "" {
stackStorePath = JoinPaths(stackStorePath, versionStr)
}
composeFilePath := JoinPaths(stackStorePath, fileName)
err := service.createBackupFileInStore(composeFilePath)
if err != nil {
return "", err
}
r := bytes.NewReader(data)
err = service.createFileInStore(composeFilePath, r)
if err != nil {
return "", err
}
return service.wrapFileStore(stackStorePath), nil
}
// RemoveStackFileBackup removes the stack file backup in the ComposeStorePath.
func (service *Service) RemoveStackFileBackup(stackIdentifier, fileName string) error {
stackStorePath := JoinPaths(ComposeStorePath, stackIdentifier)

View File

@@ -49,8 +49,8 @@ func FilterDirForEntryFile(dirEntries []DirEntry, entryFile string) []DirEntry {
return filteredDirEntries
}
// FilterDirForCompatibility returns the content of the entry file if agent version is less than 2.19.0
func FilterDirForCompatibility(dirEntries []DirEntry, entryFilePath, agentVersion string) (string, error) {
if semver.Compare(fmt.Sprintf("v%s", agentVersion), "v2.19.0") == -1 {
for _, dirEntry := range dirEntries {
if dirEntry.IsFile {

View File

@@ -9,6 +9,39 @@ import (
"github.com/portainer/portainer/api"
)
type MultiFilterArgs []struct {
FilterKey string
FilterType portainer.PerDevConfigsFilterType
}
// MultiFilterDirForPerDevConfigs filers the given dirEntries with multiple filter args, returns the merged entries for the given device
func MultiFilterDirForPerDevConfigs(dirEntries []DirEntry, configPath string, multiFilterArgs MultiFilterArgs) []DirEntry {
var filteredDirEntries []DirEntry
for _, multiFilterArg := range multiFilterArgs {
tmp := FilterDirForPerDevConfigs(dirEntries, multiFilterArg.FilterKey, configPath, multiFilterArg.FilterType)
filteredDirEntries = append(filteredDirEntries, tmp...)
}
return deduplicate(filteredDirEntries)
}
func deduplicate(dirEntries []DirEntry) []DirEntry {
var deduplicatedDirEntries []DirEntry
marks := make(map[string]struct{})
for _, dirEntry := range dirEntries {
_, ok := marks[dirEntry.Name]
if !ok {
marks[dirEntry.Name] = struct{}{}
deduplicatedDirEntries = append(deduplicatedDirEntries, dirEntry)
}
}
return deduplicatedDirEntries
}
// FilterDirForPerDevConfigs filers the given dirEntries, returns entries for the given device
// For given configPath A/B/C, return entries:
// 1. all entries outside of dir A
@@ -47,10 +80,14 @@ func shouldIncludeEntry(dirEntry DirEntry, deviceName, configPath string, filter
return shouldIncludeFile(dirEntry, deviceName, configPath)
}
// Include:
// dir entry A/B/C/<deviceName>
// all entries A/B/C/<deviceName>/*
return shouldIncludeDir(dirEntry, deviceName, configPath)
if filterType == portainer.PerDevConfigsTypeDir {
// Include:
// dir entry A/B/C/<deviceName>
// all entries A/B/C/<deviceName>/*
return shouldIncludeDir(dirEntry, deviceName, configPath)
}
return false
}
func isInConfigRootDir(dirEntry DirEntry, configPath string) bool {

View File

@@ -0,0 +1,91 @@
package filesystem
import (
portainer "github.com/portainer/portainer/api"
"github.com/stretchr/testify/assert"
"testing"
)
func TestMultiFilterDirForPerDevConfigs(t *testing.T) {
type args struct {
dirEntries []DirEntry
configPath string
multiFilterArgs MultiFilterArgs
}
baseDirEntries := []DirEntry{
{".env", "", true, 420},
{"docker-compose.yaml", "", true, 420},
{"configs", "", false, 420},
{"configs/file1.conf", "", true, 420},
{"configs/file2.conf", "", true, 420},
{"configs/folder1", "", false, 420},
{"configs/folder1/config1", "", true, 420},
{"configs/folder2", "", false, 420},
{"configs/folder2/config2", "", true, 420},
}
tests := []struct {
name string
args args
want []DirEntry
}{
{
name: "filter file1",
args: args{
baseDirEntries,
"configs",
MultiFilterArgs{{"file1", portainer.PerDevConfigsTypeFile}},
},
want: []DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[3]},
},
{
name: "filter folder1",
args: args{
baseDirEntries,
"configs",
MultiFilterArgs{{"folder1", portainer.PerDevConfigsTypeDir}},
},
want: []DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[5], baseDirEntries[6]},
},
{
name: "filter file1 and folder1",
args: args{
baseDirEntries,
"configs",
MultiFilterArgs{{"folder1", portainer.PerDevConfigsTypeDir}},
},
want: []DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[5], baseDirEntries[6]},
},
{
name: "filter file1 and file2",
args: args{
baseDirEntries,
"configs",
MultiFilterArgs{
{"file1", portainer.PerDevConfigsTypeFile},
{"file2", portainer.PerDevConfigsTypeFile},
},
},
want: []DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[3], baseDirEntries[4]},
},
{
name: "filter folder1 and folder2",
args: args{
baseDirEntries,
"configs",
MultiFilterArgs{
{"folder1", portainer.PerDevConfigsTypeDir},
{"folder2", portainer.PerDevConfigsTypeDir},
},
},
want: []DirEntry{baseDirEntries[0], baseDirEntries[1], baseDirEntries[2], baseDirEntries[5], baseDirEntries[6], baseDirEntries[7], baseDirEntries[8]},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, MultiFilterDirForPerDevConfigs(tt.args.dirEntries, tt.args.configPath, tt.args.multiFilterArgs), "MultiFilterDirForPerDevConfigs(%v, %v, %v)", tt.args.dirEntries, tt.args.configPath, tt.args.multiFilterArgs)
})
}
}

View File

@@ -3,7 +3,7 @@ package errors
import (
"errors"
httperror "github.com/portainer/libhttp/error"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
)
func TxResponse(err error, validResponse func() *httperror.HandlerError) *httperror.HandlerError {

View File

@@ -4,12 +4,12 @@ import (
"net/http"
"strings"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
httperrors "github.com/portainer/portainer/api/http/errors"
"github.com/portainer/portainer/api/internal/authorization"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/asaskevich/govalidator"
"github.com/pkg/errors"
@@ -74,7 +74,7 @@ func (handler *Handler) authenticate(rw http.ResponseWriter, r *http.Request) *h
if settings.AuthenticationMethod == portainer.AuthenticationInternal ||
settings.AuthenticationMethod == portainer.AuthenticationOAuth ||
(settings.AuthenticationMethod == portainer.AuthenticationLDAP && !settings.LDAPSettings.AutoCreateUsers) {
return &httperror.HandlerError{StatusCode: http.StatusUnprocessableEntity, Message: "Invalid credentials", Err: httperrors.ErrUnauthorized}
return httperror.NewError(http.StatusUnprocessableEntity, "Invalid credentials", httperrors.ErrUnauthorized)
}
}
@@ -83,14 +83,14 @@ func (handler *Handler) authenticate(rw http.ResponseWriter, r *http.Request) *h
}
if settings.AuthenticationMethod == portainer.AuthenticationOAuth {
return &httperror.HandlerError{StatusCode: http.StatusUnprocessableEntity, Message: "Only initial admin is allowed to login without oauth", Err: httperrors.ErrUnauthorized}
return httperror.NewError(http.StatusUnprocessableEntity, "Only initial admin is allowed to login without oauth", httperrors.ErrUnauthorized)
}
if settings.AuthenticationMethod == portainer.AuthenticationLDAP {
return handler.authenticateLDAP(rw, user, payload.Username, payload.Password, &settings.LDAPSettings)
}
return &httperror.HandlerError{StatusCode: http.StatusUnprocessableEntity, Message: "Login method is not supported", Err: httperrors.ErrUnauthorized}
return httperror.NewError(http.StatusUnprocessableEntity, "Login method is not supported", httperrors.ErrUnauthorized)
}
func isUserInitialAdmin(user *portainer.User) bool {
@@ -100,7 +100,7 @@ func isUserInitialAdmin(user *portainer.User) bool {
func (handler *Handler) authenticateInternal(w http.ResponseWriter, user *portainer.User, password string) *httperror.HandlerError {
err := handler.CryptoService.CompareHashAndData(user.Password, password)
if err != nil {
return &httperror.HandlerError{StatusCode: http.StatusUnprocessableEntity, Message: "Invalid credentials", Err: httperrors.ErrUnauthorized}
return httperror.NewError(http.StatusUnprocessableEntity, "Invalid credentials", httperrors.ErrUnauthorized)
}
forceChangePassword := !handler.passwordStrengthChecker.Check(password)

View File

@@ -4,10 +4,10 @@ import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
portainer "github.com/portainer/portainer/api"
httperrors "github.com/portainer/portainer/api/http/errors"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/asaskevich/govalidator"
"github.com/rs/zerolog/log"

View File

@@ -3,12 +3,12 @@ package auth
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/proxy"
"github.com/portainer/portainer/api/http/proxy/factory/kubernetes"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/gorilla/mux"
)

View File

@@ -3,9 +3,10 @@ package auth
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/response"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/internal/logoutcontext"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id Logout
@@ -25,5 +26,7 @@ func (handler *Handler) logout(w http.ResponseWriter, r *http.Request) *httperro
handler.KubernetesTokenCacheManager.RemoveUserFromCache(tokenData.ID)
logoutcontext.Cancel(tokenData.Token)
return response.Empty(w)
}

View File

@@ -6,9 +6,9 @@ import (
"os"
"path/filepath"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
operations "github.com/portainer/portainer/api/backup"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
)
type (

View File

@@ -4,13 +4,13 @@ import (
"context"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/portainer/api/adminmonitor"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/demo"
"github.com/portainer/portainer/api/http/middlewares"
"github.com/portainer/portainer/api/http/offlinegate"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/gorilla/mux"
)

View File

@@ -6,9 +6,9 @@ import (
"net/http"
"github.com/pkg/errors"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
operations "github.com/portainer/portainer/api/backup"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
)
type restorePayload struct {

View File

@@ -3,21 +3,23 @@ package customtemplates
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"os"
"regexp"
"strconv"
"github.com/asaskevich/govalidator"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/filesystem"
gittypes "github.com/portainer/portainer/api/git/types"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/internal/authorization"
"github.com/portainer/portainer/api/stacks/stackutils"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/asaskevich/govalidator"
"github.com/rs/zerolog/log"
)
@@ -472,3 +474,29 @@ func (handler *Handler) createCustomTemplateFromFileUpload(r *http.Request) (*po
return customTemplate, nil
}
// @id CustomTemplateCreate
// @summary Create a custom template
// @description Create a custom template.
// @description **Access policy**: authenticated
// @tags custom_templates
// @security ApiKeyAuth
// @security jwt
// @accept json,multipart/form-data
// @produce json
// @param method query string true "method for creating template" Enums(string, file, repository)
// @param body body object true "for body documentation see the relevant /custom_templates/{method} endpoint"
// @success 200 {object} portainer.CustomTemplate
// @failure 400 "Invalid request"
// @failure 500 "Server error"
// @deprecated
// @router /custom_templates [post]
func deprecatedCustomTemplateCreateUrlParser(w http.ResponseWriter, r *http.Request) (string, *httperror.HandlerError) {
method, err := request.RetrieveQueryParameter(r, "method", false)
if err != nil {
return "", httperror.BadRequest("Invalid query parameter: method", err)
}
url := fmt.Sprintf("/custom_templates/create/%s", method)
return url, nil
}

View File

@@ -4,12 +4,13 @@ import (
"net/http"
"strconv"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
httperrors "github.com/portainer/portainer/api/http/errors"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/rs/zerolog/log"
)

View File

@@ -3,10 +3,10 @@ package customtemplates
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
type fileResponse struct {

View File

@@ -6,11 +6,11 @@ import (
"os"
"sync"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/stacks/stackutils"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/rs/zerolog/log"
)

View File

@@ -4,12 +4,12 @@ import (
"net/http"
"strconv"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
httperrors "github.com/portainer/portainer/api/http/errors"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id CustomTemplateInspect

View File

@@ -5,11 +5,11 @@ import (
"strconv"
"github.com/pkg/errors"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/internal/authorization"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id CustomTemplateList

View File

@@ -6,15 +6,15 @@ import (
"net/http"
"strconv"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/filesystem"
"github.com/portainer/portainer/api/git"
gittypes "github.com/portainer/portainer/api/git/types"
httperrors "github.com/portainer/portainer/api/http/errors"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/asaskevich/govalidator"
)

View File

@@ -5,10 +5,11 @@ import (
"sync"
"github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/middlewares"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
)
// Handler is the HTTP handler used to handle environment(endpoint) group operations.
@@ -32,6 +33,7 @@ func NewHandler(bouncer security.BouncerService, dataStore dataservices.DataStor
h.Handle("/custom_templates/create/{method}",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.customTemplateCreate))).Methods(http.MethodPost)
h.Handle("/custom_templates", middlewares.Deprecated(h, deprecatedCustomTemplateCreateUrlParser)).Methods(http.MethodPost) // Deprecated
h.Handle("/custom_templates",
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.customTemplateList))).Methods(http.MethodGet)
h.Handle("/custom_templates/{id}",

View File

@@ -2,15 +2,16 @@ package containers
import (
"net/http"
"slices"
"strings"
containertypes "github.com/docker/docker/api/types/container"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/http/middlewares"
"github.com/portainer/portainer/api/internal/slices"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
containertypes "github.com/docker/docker/api/types/container"
)
type containerGpusResponse struct {

View File

@@ -4,11 +4,11 @@ import (
"net/http"
"github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/docker"
dockerclient "github.com/portainer/portainer/api/docker/client"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
)
type Handler struct {

View File

@@ -3,14 +3,14 @@ package containers
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/docker/consts"
"github.com/portainer/portainer/api/docker/images"
"github.com/portainer/portainer/api/http/middlewares"
"github.com/portainer/portainer/api/internal/authorization"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/rs/zerolog/log"
)

View File

@@ -4,17 +4,18 @@ import (
"errors"
"net/http"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/docker"
dockerclient "github.com/portainer/portainer/api/docker/client"
"github.com/portainer/portainer/api/internal/endpointutils"
"github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/handler/docker/containers"
"github.com/portainer/portainer/api/http/handler/docker/images"
"github.com/portainer/portainer/api/http/middlewares"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/internal/authorization"
"github.com/portainer/portainer/api/internal/endpointutils"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/gorilla/mux"
)
// Handler is the HTTP handler which will natively deal with to external environments(endpoints).
@@ -45,6 +46,9 @@ func NewHandler(bouncer security.BouncerService, authorizationService *authoriza
containersHandler := containers.NewHandler("/{id}/containers", bouncer, dataStore, dockerClientFactory, containerService)
endpointRouter.PathPrefix("/containers").Handler(containersHandler)
imagesHandler := images.NewHandler("/{id}/images", bouncer, dockerClientFactory)
endpointRouter.PathPrefix("/images").Handler(imagesHandler)
return h
}

View File

@@ -0,0 +1,32 @@
package images
import (
"net/http"
"github.com/portainer/portainer/api/docker/client"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/gorilla/mux"
)
type Handler struct {
*mux.Router
dockerClientFactory *client.ClientFactory
bouncer security.BouncerService
}
// NewHandler creates a handler to process non-proxied requests to docker APIs directly.
func NewHandler(routePrefix string, bouncer security.BouncerService, dockerClientFactory *client.ClientFactory) *Handler {
h := &Handler{
Router: mux.NewRouter(),
dockerClientFactory: dockerClientFactory,
bouncer: bouncer,
}
router := h.PathPrefix(routePrefix).Subrouter()
router.Use(bouncer.AuthenticatedAccess)
router.Handle("", httperror.LoggerHandler(h.imagesList)).Methods(http.MethodGet)
return h
}

View File

@@ -0,0 +1,79 @@
package images
import (
"net/http"
"github.com/docker/docker/api/types"
"github.com/portainer/portainer/api/http/handler/docker/utils"
"github.com/portainer/portainer/api/internal/set"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
type ImageResponse struct {
Created int64 `json:"created"`
NodeName string `json:"nodeName"`
ID string `json:"id"`
Size int64 `json:"size"`
Tags []string `json:"tags"`
// Used is true if the image is used by at least one container
// supplied only when withUsage is true
Used bool `json:"used"`
}
// @id dockerImagesList
// @summary Fetch images
// @description
// @description **Access policy**:
// @tags docker
// @security jwt
// @param environmentId path int true "Environment identifier"
// @param withUsage query boolean false "Include image usage information"
// @produce json
// @success 200 {array} ImageResponse "Success"
// @failure 400 "Bad request"
// @failure 500 "Internal server error"
// @router /docker/{environmentId}/images [get]
func (handler *Handler) imagesList(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
cli, httpErr := utils.GetClient(r, handler.dockerClientFactory)
if httpErr != nil {
return httpErr
}
images, err := cli.ImageList(r.Context(), types.ImageListOptions{})
if err != nil {
return httperror.InternalServerError("Unable to retrieve Docker images", err)
}
withUsage, err := request.RetrieveBooleanQueryParameter(r, "withUsage", true)
if err != nil {
return httperror.BadRequest("Invalid query parameter: withUsage", err)
}
imageUsageSet := set.Set[string]{}
if withUsage {
containers, err := cli.ContainerList(r.Context(), types.ContainerListOptions{})
if err != nil {
return httperror.InternalServerError("Unable to retrieve Docker containers", err)
}
for _, container := range containers {
imageUsageSet.Add(container.ImageID)
}
}
imagesList := make([]ImageResponse, len(images))
for i, image := range images {
imagesList[i] = ImageResponse{
Created: image.Created,
ID: image.ID,
Size: image.Size,
Tags: image.RepoTags,
Used: imageUsageSet.Contains(image.ID),
}
}
return response.JSON(w, imagesList)
}

View File

@@ -0,0 +1,28 @@
package utils
import (
"net/http"
dockerclient "github.com/docker/docker/client"
portainer "github.com/portainer/portainer/api"
prclient "github.com/portainer/portainer/api/docker/client"
"github.com/portainer/portainer/api/http/middlewares"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
)
// GetClient returns a Docker client based on the request context
func GetClient(r *http.Request, dockerClientFactory *prclient.ClientFactory) (*dockerclient.Client, *httperror.HandlerError) {
endpoint, err := middlewares.FetchEndpoint(r)
if err != nil {
return nil, httperror.NotFound("Unable to find an environment on request context", err)
}
agentTargetHeader := r.Header.Get(portainer.PortainerAgentTargetHeader)
cli, err := dockerClientFactory.CreateClient(endpoint, agentTargetHeader, nil)
if err != nil {
return nil, httperror.InternalServerError("Unable to connect to the Docker daemon", err)
}
return cli, nil
}

View File

@@ -49,6 +49,24 @@ func GetEndpointsByTags(tx dataservices.DataStoreTx, tagIDs []portainer.TagID, p
return results, nil
}
func getTrustedEndpoints(tx dataservices.DataStoreTx, endpointIDs []portainer.EndpointID) ([]portainer.EndpointID, error) {
results := []portainer.EndpointID{}
for _, endpointID := range endpointIDs {
endpoint, err := tx.Endpoint().Endpoint(endpointID)
if err != nil {
return nil, err
}
if !endpoint.UserTrusted {
continue
}
results = append(results, endpoint.ID)
}
return results, nil
}
func mapEndpointGroupToEndpoints(endpoints []portainer.Endpoint) map[portainer.EndpointGroupID]endpointSetType {
groupEndpoints := map[portainer.EndpointGroupID]endpointSetType{}

View File

@@ -4,11 +4,11 @@ import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/endpointutils"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/asaskevich/govalidator"
)

View File

@@ -4,12 +4,11 @@ import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id EdgeGroupDelete
@@ -29,14 +28,9 @@ func (handler *Handler) edgeGroupDelete(w http.ResponseWriter, r *http.Request)
return httperror.BadRequest("Invalid Edge group identifier route variable", err)
}
if featureflags.IsEnabled(portainer.FeatureNoTx) {
err = deleteEdgeGroup(handler.DataStore, portainer.EdgeGroupID(edgeGroupID))
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return deleteEdgeGroup(tx, portainer.EdgeGroupID(edgeGroupID))
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return deleteEdgeGroup(tx, portainer.EdgeGroupID(edgeGroupID))
})
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
@@ -65,7 +59,7 @@ func deleteEdgeGroup(tx dataservices.DataStoreTx, ID portainer.EdgeGroupID) erro
for _, edgeStack := range edgeStacks {
for _, groupID := range edgeStack.EdgeGroups {
if groupID == ID {
return httperror.NewError(http.StatusConflict, "Edge group is used by an Edge stack", errors.New("edge group is used by an Edge stack"))
return httperror.Conflict("Edge group is used by an Edge stack", errors.New("edge group is used by an Edge stack"))
}
}
}
@@ -78,7 +72,7 @@ func deleteEdgeGroup(tx dataservices.DataStoreTx, ID portainer.EdgeGroupID) erro
for _, edgeJob := range edgeJobs {
for _, groupID := range edgeJob.EdgeGroups {
if groupID == ID {
return httperror.NewError(http.StatusConflict, "Edge group is used by an Edge job", errors.New("edge group is used by an Edge job"))
return httperror.Conflict("Edge group is used by an Edge job", errors.New("edge group is used by an Edge job"))
}
}
}

View File

@@ -3,11 +3,10 @@ package edgegroups
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
)
// @id EdgeGroupInspect
@@ -29,14 +28,10 @@ func (handler *Handler) edgeGroupInspect(w http.ResponseWriter, r *http.Request)
}
var edgeGroup *portainer.EdgeGroup
if featureflags.IsEnabled(portainer.FeatureNoTx) {
edgeGroup, err = getEdgeGroup(handler.DataStore, portainer.EdgeGroupID(edgeGroupID))
} else {
err = handler.DataStore.ViewTx(func(tx dataservices.DataStoreTx) error {
edgeGroup, err = getEdgeGroup(tx, portainer.EdgeGroupID(edgeGroupID))
return err
})
}
err = handler.DataStore.ViewTx(func(tx dataservices.DataStoreTx) error {
edgeGroup, err = getEdgeGroup(tx, portainer.EdgeGroupID(edgeGroupID))
return err
})
return txResponse(w, edgeGroup, err)
}

View File

@@ -3,19 +3,19 @@ package edgegroups
import (
"fmt"
"net/http"
"slices"
httperror "github.com/portainer/libhttp/error"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/slices"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
)
type decoratedEdgeGroup struct {
portainer.EdgeGroup
HasEdgeStack bool `json:"HasEdgeStack"`
HasEdgeJob bool `json:"HasEdgeJob"`
EndpointTypes []portainer.EndpointType
HasEdgeStack bool `json:"HasEdgeStack"`
HasEdgeJob bool `json:"HasEdgeJob"`
EndpointTypes []portainer.EndpointType
TrustedEndpoints []portainer.EndpointID `json:"TrustedEndpoints"`
}
// @id EdgeGroupList
@@ -33,14 +33,10 @@ func (handler *Handler) edgeGroupList(w http.ResponseWriter, r *http.Request) *h
var decoratedEdgeGroups []decoratedEdgeGroup
var err error
if featureflags.IsEnabled(portainer.FeatureNoTx) {
decoratedEdgeGroups, err = getEdgeGroupList(handler.DataStore)
} else {
err = handler.DataStore.ViewTx(func(tx dataservices.DataStoreTx) error {
decoratedEdgeGroups, err = getEdgeGroupList(tx)
return err
})
}
err = handler.DataStore.ViewTx(func(tx dataservices.DataStoreTx) error {
decoratedEdgeGroups, err = getEdgeGroupList(tx)
return err
})
return txResponse(w, decoratedEdgeGroups, err)
}
@@ -90,6 +86,14 @@ func getEdgeGroupList(tx dataservices.DataStoreTx) ([]decoratedEdgeGroup, error)
}
edgeGroup.Endpoints = endpointIDs
edgeGroup.TrustedEndpoints = endpointIDs
} else {
trustedEndpoints, err := getTrustedEndpoints(tx, edgeGroup.Endpoints)
if err != nil {
return nil, httperror.InternalServerError("Unable to retrieve environments for Edge group", err)
}
edgeGroup.TrustedEndpoints = trustedEndpoints
}
endpointTypes, err := getEndpointTypes(tx, edgeGroup.Endpoints)

View File

@@ -3,15 +3,15 @@ package edgegroups
import (
"errors"
"net/http"
"slices"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/edge"
"github.com/portainer/portainer/api/internal/endpointutils"
"github.com/portainer/portainer/api/internal/slices"
"github.com/portainer/portainer/api/internal/unique"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/asaskevich/govalidator"
)

View File

@@ -4,11 +4,11 @@ import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/gorilla/mux"
)

View File

@@ -2,19 +2,19 @@ package edgejobs
import (
"errors"
"fmt"
"maps"
"net/http"
"strconv"
"strings"
"time"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/edge"
"github.com/portainer/portainer/api/internal/endpointutils"
"github.com/portainer/portainer/api/internal/maps"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/asaskevich/govalidator"
)
@@ -92,15 +92,11 @@ func (handler *Handler) createEdgeJobFromFileContent(w http.ResponseWriter, r *h
}
var edgeJob *portainer.EdgeJob
if featureflags.IsEnabled(portainer.FeatureNoTx) {
edgeJob, err = handler.createEdgeJob(handler.DataStore, &payload.edgeJobBasePayload, []byte(payload.FileContent))
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
edgeJob, err = handler.createEdgeJob(tx, &payload.edgeJobBasePayload, []byte(payload.FileContent))
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
edgeJob, err = handler.createEdgeJob(tx, &payload.edgeJobBasePayload, []byte(payload.FileContent))
return err
})
}
return err
})
return txResponse(w, edgeJob, err)
}
@@ -201,15 +197,11 @@ func (handler *Handler) createEdgeJobFromFile(w http.ResponseWriter, r *http.Req
}
var edgeJob *portainer.EdgeJob
if featureflags.IsEnabled(portainer.FeatureNoTx) {
edgeJob, err = handler.createEdgeJob(handler.DataStore, &payload.edgeJobBasePayload, payload.File)
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
edgeJob, err = handler.createEdgeJob(tx, &payload.edgeJobBasePayload, payload.File)
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
edgeJob, err = handler.createEdgeJob(tx, &payload.edgeJobBasePayload, payload.File)
return err
})
}
return err
})
return txResponse(w, edgeJob, err)
}
@@ -287,3 +279,26 @@ func (handler *Handler) addAndPersistEdgeJob(tx dataservices.DataStoreTx, edgeJo
return tx.EdgeJob().CreateWithID(edgeJob.ID, edgeJob)
}
// @id EdgeJobCreate
// @summary Create an EdgeJob
// @description **Access policy**: administrator
// @tags edge_jobs
// @security ApiKeyAuth
// @security jwt
// @produce json
// @param method query string true "Creation Method" Enums(file, string)
// @param body body object true "for body documentation see the relevant /edge_jobs/create/{method} endpoint"
// @success 200 {object} portainer.EdgeGroup
// @failure 503 "Edge compute features are disabled"
// @failure 500
// @deprecated
// @router /edge_jobs [post]
func deprecatedEdgeJobCreateUrlParser(w http.ResponseWriter, r *http.Request) (string, *httperror.HandlerError) {
method, err := request.RetrieveQueryParameter(r, "method", false)
if err != nil {
return "", httperror.BadRequest("Invalid query parameter: method. Valid values are: file or string", err)
}
return fmt.Sprintf("/edge_jobs/create/%s", method), nil
}

View File

@@ -2,17 +2,16 @@ package edgejobs
import (
"errors"
"maps"
"net/http"
"strconv"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/edge"
"github.com/portainer/portainer/api/internal/maps"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/rs/zerolog/log"
)
@@ -34,14 +33,9 @@ func (handler *Handler) edgeJobDelete(w http.ResponseWriter, r *http.Request) *h
return httperror.BadRequest("Invalid Edge job identifier route variable", err)
}
if featureflags.IsEnabled(portainer.FeatureNoTx) {
err = handler.deleteEdgeJob(handler.DataStore, portainer.EdgeJobID(edgeJobID))
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.deleteEdgeJob(tx, portainer.EdgeJobID(edgeJobID))
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.deleteEdgeJob(tx, portainer.EdgeJobID(edgeJobID))
})
if err != nil {
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {

View File

@@ -3,10 +3,10 @@ package edgejobs
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
type edgeJobFileResponse struct {

View File

@@ -3,10 +3,10 @@ package edgejobs
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
type edgeJobInspectResponse struct {

View File

@@ -3,8 +3,8 @@ package edgejobs
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/response"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id EdgeJobList

View File

@@ -3,16 +3,15 @@ package edgejobs
import (
"errors"
"net/http"
"slices"
"strconv"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/edge"
"github.com/portainer/portainer/api/internal/slices"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id EdgeJobTasksClear
@@ -54,27 +53,15 @@ func (handler *Handler) edgeJobTasksClear(w http.ResponseWriter, r *http.Request
}
}
if featureflags.IsEnabled(portainer.FeatureNoTx) {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
updateEdgeJobFn := func(edgeJob *portainer.EdgeJob, endpointID portainer.EndpointID, endpointsFromGroups []portainer.EndpointID) error {
return handler.DataStore.EdgeJob().UpdateEdgeJobFunc(edgeJob.ID, func(j *portainer.EdgeJob) {
mutationFn(j, endpointID, endpointsFromGroups)
})
mutationFn(edgeJob, endpointID, endpointsFromGroups)
return tx.EdgeJob().Update(edgeJob.ID, edgeJob)
}
err = handler.clearEdgeJobTaskLogs(handler.DataStore, portainer.EdgeJobID(edgeJobID), portainer.EndpointID(taskID), updateEdgeJobFn)
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
updateEdgeJobFn := func(edgeJob *portainer.EdgeJob, endpointID portainer.EndpointID, endpointsFromGroups []portainer.EndpointID) error {
mutationFn(edgeJob, endpointID, endpointsFromGroups)
return tx.EdgeJob().Update(edgeJob.ID, edgeJob)
}
return handler.clearEdgeJobTaskLogs(tx, portainer.EdgeJobID(edgeJobID), portainer.EndpointID(taskID), updateEdgeJobFn)
})
}
return handler.clearEdgeJobTaskLogs(tx, portainer.EdgeJobID(edgeJobID), portainer.EndpointID(taskID), updateEdgeJobFn)
})
if err != nil {
var handlerError *httperror.HandlerError
if errors.As(err, &handlerError) {
@@ -110,11 +97,6 @@ func (handler *Handler) clearEdgeJobTaskLogs(tx dataservices.DataStoreTx, edgeJo
return httperror.InternalServerError("Unable to persist Edge job changes in the database", err)
}
err = handler.FileService.ClearEdgeJobTaskLogs(strconv.Itoa(int(edgeJobID)), strconv.Itoa(int(endpointID)))
if err != nil {
return httperror.InternalServerError("Unable to clear log file from disk", err)
}
endpoint, err := tx.Endpoint().Endpoint(endpointID)
if err != nil {
return httperror.NotFound("Unable to retrieve environment from the database", err)

View File

@@ -3,14 +3,14 @@ package edgejobs
import (
"errors"
"net/http"
"slices"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/edge"
"github.com/portainer/portainer/api/internal/slices"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id EdgeJobTasksCollect

View File

@@ -4,9 +4,9 @@ import (
"net/http"
"strconv"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
type fileResponse struct {

View File

@@ -2,15 +2,14 @@ package edgejobs
import (
"fmt"
"maps"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/edge"
"github.com/portainer/portainer/api/internal/maps"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
)
type taskContainer struct {
@@ -39,14 +38,10 @@ func (handler *Handler) edgeJobTasksList(w http.ResponseWriter, r *http.Request)
}
var tasks []taskContainer
if featureflags.IsEnabled(portainer.FeatureNoTx) {
tasks, err = listEdgeJobTasks(handler.DataStore, portainer.EdgeJobID(edgeJobID))
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
tasks, err = listEdgeJobTasks(tx, portainer.EdgeJobID(edgeJobID))
return err
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
tasks, err = listEdgeJobTasks(tx, portainer.EdgeJobID(edgeJobID))
return err
})
return txResponse(w, tasks, err)
}

View File

@@ -2,18 +2,17 @@ package edgejobs
import (
"errors"
"maps"
"net/http"
"slices"
"strconv"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/edge"
"github.com/portainer/portainer/api/internal/endpointutils"
"github.com/portainer/portainer/api/internal/maps"
"github.com/portainer/portainer/api/internal/slices"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/asaskevich/govalidator"
)
@@ -63,14 +62,10 @@ func (handler *Handler) edgeJobUpdate(w http.ResponseWriter, r *http.Request) *h
}
var edgeJob *portainer.EdgeJob
if featureflags.IsEnabled(portainer.FeatureNoTx) {
edgeJob, err = handler.updateEdgeJob(handler.DataStore, portainer.EdgeJobID(edgeJobID), payload)
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
edgeJob, err = handler.updateEdgeJob(tx, portainer.EdgeJobID(edgeJobID), payload)
return err
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
edgeJob, err = handler.updateEdgeJob(tx, portainer.EdgeJobID(edgeJobID), payload)
return err
})
return txResponse(w, edgeJob, err)
}

View File

@@ -4,11 +4,12 @@ import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/middlewares"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/gorilla/mux"
)
@@ -29,6 +30,8 @@ func NewHandler(bouncer security.BouncerService) *Handler {
h.Handle("/edge_jobs",
bouncer.AdminAccess(bouncer.EdgeComputeOperation(httperror.LoggerHandler(h.edgeJobList)))).Methods(http.MethodGet)
h.Handle("/edge_jobs",
bouncer.AdminAccess(bouncer.EdgeComputeOperation(middlewares.Deprecated(h, deprecatedEdgeJobCreateUrlParser)))).Methods(http.MethodPost)
h.Handle("/edge_jobs/create/{method}",
bouncer.AdminAccess(bouncer.EdgeComputeOperation(httperror.LoggerHandler(h.edgeJobCreate)))).Methods(http.MethodPost)
h.Handle("/edge_jobs/{id}",

View File

@@ -1,16 +1,16 @@
package edgestacks
import (
"fmt"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
httperrors "github.com/portainer/portainer/api/http/errors"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
func (handler *Handler) edgeStackCreate(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
@@ -18,6 +18,7 @@ func (handler *Handler) edgeStackCreate(w http.ResponseWriter, r *http.Request)
if err != nil {
return httperror.BadRequest("Invalid query parameter: method", err)
}
dryrun, _ := request.RetrieveBooleanQueryParameter(r, "dryrun", true)
tokenData, err := security.RetrieveTokenData(r)
@@ -26,20 +27,16 @@ func (handler *Handler) edgeStackCreate(w http.ResponseWriter, r *http.Request)
}
var edgeStack *portainer.EdgeStack
if featureflags.IsEnabled(portainer.FeatureNoTx) {
edgeStack, err = handler.createSwarmStack(handler.DataStore, method, dryrun, tokenData.ID, r)
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
edgeStack, err = handler.createSwarmStack(tx, method, dryrun, tokenData.ID, r)
return err
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
edgeStack, err = handler.createSwarmStack(tx, method, dryrun, tokenData.ID, r)
return err
})
if err != nil {
switch {
case httperrors.IsInvalidPayloadError(err):
return httperror.BadRequest("Invalid payload", err)
case httperrors.IsConflictError(err):
return httperror.NewError(http.StatusConflict, err.Error(), err)
return httperror.Conflict(err.Error(), err)
default:
return httperror.InternalServerError("Unable to create Edge stack", err)
}
@@ -60,3 +57,26 @@ func (handler *Handler) createSwarmStack(tx dataservices.DataStoreTx, method str
return nil, httperrors.NewInvalidPayloadError("Invalid value for query parameter: method. Value must be one of: string, repository or file")
}
// @id EdgeStackCreate
// @summary Create an EdgeStack
// @description **Access policy**: administrator
// @tags edge_stacks
// @security ApiKeyAuth
// @security jwt
// @produce json
// @param method query string true "Creation Method" Enums(file,string,repository)
// @param body body object true "for body documentation see the relevant /edge_stacks/create/{method} endpoint"
// @success 200 {object} portainer.EdgeStack
// @failure 500
// @failure 503 "Edge compute features are disabled"
// @deprecated
// @router /edge_stacks [post]
func deprecatedEdgeStackCreateUrlParser(w http.ResponseWriter, r *http.Request) (string, *httperror.HandlerError) {
method, err := request.RetrieveQueryParameter(r, "method", false)
if err != nil {
return "", httperror.BadRequest("Invalid query parameter: method. Valid values are: file or string", err)
}
return fmt.Sprintf("/edge_stacks/create/%s", method), nil
}

View File

@@ -3,10 +3,10 @@ package edgestacks
import (
"net/http"
"github.com/portainer/libhttp/request"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
httperrors "github.com/portainer/portainer/api/http/errors"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/pkg/errors"
)

View File

@@ -4,12 +4,12 @@ import (
"fmt"
"net/http"
"github.com/portainer/libhttp/request"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/filesystem"
gittypes "github.com/portainer/portainer/api/git/types"
httperrors "github.com/portainer/portainer/api/http/errors"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/asaskevich/govalidator"
"github.com/pkg/errors"

View File

@@ -4,11 +4,11 @@ import (
"fmt"
"net/http"
"github.com/portainer/libhttp/request"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/filesystem"
httperrors "github.com/portainer/portainer/api/http/errors"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/asaskevich/govalidator"
"github.com/pkg/errors"

View File

@@ -4,12 +4,11 @@ import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id EdgeStackDelete
@@ -30,14 +29,9 @@ func (handler *Handler) edgeStackDelete(w http.ResponseWriter, r *http.Request)
return httperror.BadRequest("Invalid edge stack identifier route variable", err)
}
if featureflags.IsEnabled(portainer.FeatureNoTx) {
err = handler.deleteEdgeStack(handler.DataStore, portainer.EdgeStackID(edgeStackID))
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.deleteEdgeStack(tx, portainer.EdgeStackID(edgeStackID))
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.deleteEdgeStack(tx, portainer.EdgeStackID(edgeStackID))
})
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {

View File

@@ -3,10 +3,10 @@ package edgestacks
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
type stackFileResponse struct {

View File

@@ -3,10 +3,10 @@ package edgestacks
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id EdgeStackInspect

View File

@@ -3,8 +3,8 @@ package edgestacks
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/response"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id EdgeStackList

View File

@@ -5,13 +5,12 @@ import (
"net/http"
"time"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/middlewares"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id EdgeStackStatusDelete
@@ -45,14 +44,10 @@ func (handler *Handler) edgeStackStatusDelete(w http.ResponseWriter, r *http.Req
}
var stack *portainer.EdgeStack
if featureflags.IsEnabled(portainer.FeatureNoTx) {
stack, err = handler.deleteEdgeStackStatus(handler.DataStore, portainer.EdgeStackID(stackID), endpoint)
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
stack, err = handler.deleteEdgeStackStatus(tx, portainer.EdgeStackID(stackID), endpoint)
return err
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
stack, err = handler.deleteEdgeStackStatus(tx, portainer.EdgeStackID(stackID), endpoint)
return err
})
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {

View File

@@ -5,15 +5,14 @@ import (
"net/http"
"time"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/pkg/featureflags"
"github.com/rs/zerolog/log"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/asaskevich/govalidator"
"github.com/rs/zerolog/log"
)
type updateStatusPayload struct {
@@ -70,15 +69,10 @@ func (handler *Handler) edgeStackStatusUpdate(w http.ResponseWriter, r *http.Req
}
var stack *portainer.EdgeStack
if featureflags.IsEnabled(portainer.FeatureNoTx) {
stack, err = handler.updateEdgeStackStatus(handler.DataStore, r, portainer.EdgeStackID(stackID), payload)
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
stack, err = handler.updateEdgeStackStatus(tx, r, portainer.EdgeStackID(stackID), payload)
return err
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
stack, err = handler.updateEdgeStackStatus(tx, r, portainer.EdgeStackID(stackID), payload)
return err
})
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
@@ -130,22 +124,11 @@ func (handler *Handler) updateEdgeStackStatus(tx dataservices.DataStoreTx, r *ht
Time: payload.Time,
}
if featureflags.IsEnabled(portainer.FeatureNoTx) {
err = tx.EdgeStack().UpdateEdgeStackFunc(stackID, func(edgeStack *portainer.EdgeStack) {
updateEnvStatus(payload.EndpointID, edgeStack, deploymentStatus)
updateEnvStatus(payload.EndpointID, stack, deploymentStatus)
stack = edgeStack
})
if err != nil {
return nil, handler.handlerDBErr(err, "Unable to persist the stack changes inside the database")
}
} else {
updateEnvStatus(payload.EndpointID, stack, deploymentStatus)
err = tx.EdgeStack().UpdateEdgeStack(stackID, stack)
if err != nil {
return nil, handler.handlerDBErr(err, "Unable to persist the stack changes inside the database")
}
err = tx.EdgeStack().UpdateEdgeStack(stackID, stack)
if err != nil {
return nil, handler.handlerDBErr(err, "Unable to persist the stack changes inside the database")
}
return stack, nil

View File

@@ -4,15 +4,15 @@ import (
"net/http"
"time"
"github.com/pkg/errors"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/edge"
"github.com/portainer/portainer/api/internal/set"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/pkg/errors"
)
type updateEdgeStackPayload struct {
@@ -64,15 +64,10 @@ func (handler *Handler) edgeStackUpdate(w http.ResponseWriter, r *http.Request)
}
var stack *portainer.EdgeStack
if featureflags.IsEnabled(portainer.FeatureNoTx) {
stack, err = handler.updateEdgeStack(handler.DataStore, portainer.EdgeStackID(stackID), payload)
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
stack, err = handler.updateEdgeStack(tx, portainer.EdgeStackID(stackID), payload)
return err
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
stack, err = handler.updateEdgeStack(tx, portainer.EdgeStackID(stackID), payload)
return err
})
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {

View File

@@ -4,14 +4,15 @@ import (
"fmt"
"net/http"
"github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/filesystem"
"github.com/portainer/portainer/api/http/middlewares"
"github.com/portainer/portainer/api/http/security"
edgestackservice "github.com/portainer/portainer/api/internal/edge/edgestacks"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/gorilla/mux"
)
// Handler is the HTTP handler used to handle environment(endpoint) group operations.
@@ -38,6 +39,8 @@ func NewHandler(bouncer security.BouncerService, dataStore dataservices.DataStor
h.Handle("/edge_stacks/create/{method}",
bouncer.AdminAccess(bouncer.EdgeComputeOperation(httperror.LoggerHandler(h.edgeStackCreate)))).Methods(http.MethodPost)
h.Handle("/edge_stacks",
bouncer.AdminAccess(bouncer.EdgeComputeOperation(middlewares.Deprecated(h, deprecatedEdgeStackCreateUrlParser)))).Methods(http.MethodPost) // Deprecated
h.Handle("/edge_stacks",
bouncer.AdminAccess(bouncer.EdgeComputeOperation(httperror.LoggerHandler(h.edgeStackList)))).Methods(http.MethodGet)
h.Handle("/edge_stacks/{id}",

View File

@@ -4,10 +4,10 @@ import (
"encoding/json"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/http/client"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/response"
)
type templateFileFormat struct {

View File

@@ -3,11 +3,11 @@ package edgetemplates
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/gorilla/mux"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/gorilla/mux"
)
// Handler is the HTTP handler used to handle edge environment(endpoint) operations.

View File

@@ -5,13 +5,12 @@ import (
"net/http"
"strconv"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/middlewares"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
type logsPayload struct {
@@ -56,14 +55,9 @@ func (handler *Handler) endpointEdgeJobsLogs(w http.ResponseWriter, r *http.Requ
return httperror.BadRequest("Invalid request payload", err)
}
if featureflags.IsEnabled(portainer.FeatureNoTx) {
err = handler.getEdgeJobLobs(handler.DataStore, endpoint.ID, portainer.EdgeJobID(edgeJobID), payload)
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.getEdgeJobLobs(tx, endpoint.ID, portainer.EdgeJobID(edgeJobID), payload)
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.getEdgeJobLobs(tx, endpoint.ID, portainer.EdgeJobID(edgeJobID), payload)
})
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {

View File

@@ -2,17 +2,17 @@ package endpointedge
import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/edge"
"github.com/portainer/portainer/api/filesystem"
"github.com/portainer/portainer/api/http/middlewares"
"github.com/portainer/portainer/api/internal/endpointutils"
"github.com/portainer/portainer/api/kubernetes"
"net/http"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @summary Inspect an Edge Stack for an Environment(Endpoint)

View File

@@ -13,13 +13,12 @@ import (
"strings"
"time"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/edge/cache"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
type stackStatusResponse struct {
@@ -106,15 +105,10 @@ func (handler *Handler) endpointEdgeStatusInspect(w http.ResponseWriter, r *http
}
var statusResponse *endpointEdgeStatusInspectResponse
if featureflags.IsEnabled(portainer.FeatureNoTx) {
statusResponse, err = handler.inspectStatus(handler.DataStore, r, portainer.EndpointID(endpointID))
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
statusResponse, err = handler.inspectStatus(tx, r, portainer.EndpointID(endpointID))
return err
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
statusResponse, err = handler.inspectStatus(tx, r, portainer.EndpointID(endpointID))
return err
})
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {

View File

@@ -3,11 +3,11 @@ package endpointedge
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/middlewares"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/gorilla/mux"
)

View File

@@ -4,12 +4,11 @@ import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/asaskevich/govalidator"
)
@@ -58,15 +57,10 @@ func (handler *Handler) endpointGroupCreate(w http.ResponseWriter, r *http.Reque
}
var endpointGroup *portainer.EndpointGroup
if featureflags.IsEnabled(portainer.FeatureNoTx) {
endpointGroup, err = handler.createEndpointGroup(handler.DataStore, payload)
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
endpointGroup, err = handler.createEndpointGroup(tx, payload)
return err
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
endpointGroup, err = handler.createEndpointGroup(tx, payload)
return err
})
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
@@ -119,20 +113,6 @@ func (handler *Handler) createEndpointGroup(tx dataservices.DataStoreTx, payload
}
for _, tagID := range endpointGroup.TagIDs {
if featureflags.IsEnabled(portainer.FeatureNoTx) {
err = tx.Tag().UpdateTagFunc(tagID, func(tag *portainer.Tag) {
tag.EndpointGroups[endpointGroup.ID] = true
})
if tx.IsErrObjectNotFound(err) {
return nil, httperror.InternalServerError("Unable to find a tag inside the database", err)
} else if err != nil {
return nil, httperror.InternalServerError("Unable to persist tag changes inside the database", err)
}
continue
}
tag, err := tx.Tag().Read(tagID)
if err != nil {
return nil, httperror.InternalServerError("Unable to find a tag inside the database", err)

View File

@@ -4,12 +4,11 @@ import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id EndpointGroupDelete
@@ -35,14 +34,9 @@ func (handler *Handler) endpointGroupDelete(w http.ResponseWriter, r *http.Reque
return httperror.Forbidden("Unable to remove the default 'Unassigned' group", errors.New("Cannot remove the default environment group"))
}
if featureflags.IsEnabled(portainer.FeatureNoTx) {
err = handler.deleteEndpointGroup(handler.DataStore, portainer.EndpointGroupID(endpointGroupID))
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.deleteEndpointGroup(tx, portainer.EndpointGroupID(endpointGroupID))
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.deleteEndpointGroup(tx, portainer.EndpointGroupID(endpointGroupID))
})
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
@@ -89,20 +83,6 @@ func (handler *Handler) deleteEndpointGroup(tx dataservices.DataStoreTx, endpoin
}
for _, tagID := range endpointGroup.TagIDs {
if featureflags.IsEnabled(portainer.FeatureNoTx) {
err = tx.Tag().UpdateTagFunc(tagID, func(tag *portainer.Tag) {
delete(tag.EndpointGroups, endpointGroup.ID)
})
if tx.IsErrObjectNotFound(err) {
return httperror.InternalServerError("Unable to find a tag inside the database", err)
} else if err != nil {
return httperror.InternalServerError("Unable to persist tag changes inside the database", err)
}
continue
}
tag, err := tx.Tag().Read(tagID)
if tx.IsErrObjectNotFound(err) {
return httperror.InternalServerError("Unable to find a tag inside the database", err)

View File

@@ -4,12 +4,11 @@ import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id EndpointGroupAddEndpoint
@@ -37,14 +36,9 @@ func (handler *Handler) endpointGroupAddEndpoint(w http.ResponseWriter, r *http.
return httperror.BadRequest("Invalid environment identifier route variable", err)
}
if featureflags.IsEnabled(portainer.FeatureNoTx) {
err = handler.addEndpoint(handler.DataStore, portainer.EndpointGroupID(endpointGroupID), portainer.EndpointID(endpointID))
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.addEndpoint(tx, portainer.EndpointGroupID(endpointGroupID), portainer.EndpointID(endpointID))
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.addEndpoint(tx, portainer.EndpointGroupID(endpointGroupID), portainer.EndpointID(endpointID))
})
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {

View File

@@ -4,12 +4,11 @@ import (
"errors"
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id EndpointGroupDeleteEndpoint
@@ -36,14 +35,9 @@ func (handler *Handler) endpointGroupDeleteEndpoint(w http.ResponseWriter, r *ht
return httperror.BadRequest("Invalid environment identifier route variable", err)
}
if featureflags.IsEnabled(portainer.FeatureNoTx) {
err = handler.removeEndpoint(handler.DataStore, portainer.EndpointGroupID(endpointGroupID), portainer.EndpointID(endpointID))
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.removeEndpoint(tx, portainer.EndpointGroupID(endpointGroupID), portainer.EndpointID(endpointID))
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
return handler.removeEndpoint(tx, portainer.EndpointGroupID(endpointGroupID), portainer.EndpointID(endpointID))
})
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {

View File

@@ -3,10 +3,10 @@ package endpointgroups
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @summary Inspect an Environment(Endpoint) group

View File

@@ -3,9 +3,9 @@ package endpointgroups
import (
"net/http"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/response"
"github.com/portainer/portainer/api/http/security"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/response"
)
// @id EndpointGroupList

View File

@@ -5,13 +5,13 @@ import (
"net/http"
"reflect"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/libhttp/request"
"github.com/portainer/libhttp/response"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/internal/tag"
"github.com/portainer/portainer/pkg/featureflags"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/portainer/portainer/pkg/libhttp/request"
"github.com/portainer/portainer/pkg/libhttp/response"
"github.com/rs/zerolog/log"
)
type endpointGroupUpdatePayload struct {
@@ -58,15 +58,10 @@ func (handler *Handler) endpointGroupUpdate(w http.ResponseWriter, r *http.Reque
}
var endpointGroup *portainer.EndpointGroup
if featureflags.IsEnabled(portainer.FeatureNoTx) {
endpointGroup, err = handler.updateEndpointGroup(handler.DataStore, portainer.EndpointGroupID(endpointGroupID), payload)
} else {
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
endpointGroup, err = handler.updateEndpointGroup(tx, portainer.EndpointGroupID(endpointGroupID), payload)
return err
})
}
err = handler.DataStore.UpdateTx(func(tx dataservices.DataStoreTx) error {
endpointGroup, err = handler.updateEndpointGroup(tx, portainer.EndpointGroupID(endpointGroupID), payload)
return err
})
if err != nil {
var httpErr *httperror.HandlerError
if errors.As(err, &httpErr) {
@@ -107,20 +102,6 @@ func (handler *Handler) updateEndpointGroup(tx dataservices.DataStoreTx, endpoin
removeTags := tag.Difference(endpointGroupTagSet, payloadTagSet)
for tagID := range removeTags {
if featureflags.IsEnabled(portainer.FeatureNoTx) {
err = tx.Tag().UpdateTagFunc(tagID, func(tag *portainer.Tag) {
delete(tag.EndpointGroups, endpointGroup.ID)
})
if tx.IsErrObjectNotFound(err) {
return nil, httperror.InternalServerError("Unable to find a tag inside the database", err)
} else if err != nil {
return nil, httperror.InternalServerError("Unable to persist tag changes inside the database", err)
}
continue
}
tag, err := tx.Tag().Read(tagID)
if err != nil {
return nil, httperror.InternalServerError("Unable to find a tag inside the database", err)
@@ -136,20 +117,6 @@ func (handler *Handler) updateEndpointGroup(tx dataservices.DataStoreTx, endpoin
endpointGroup.TagIDs = payload.TagIDs
for _, tagID := range payload.TagIDs {
if featureflags.IsEnabled(portainer.FeatureNoTx) {
err = tx.Tag().UpdateTagFunc(tagID, func(tag *portainer.Tag) {
tag.EndpointGroups[endpointGroup.ID] = true
})
if tx.IsErrObjectNotFound(err) {
return nil, httperror.InternalServerError("Unable to find a tag inside the database", err)
} else if err != nil {
return nil, httperror.InternalServerError("Unable to persist tag changes inside the database", err)
}
continue
}
tag, err := tx.Tag().Read(tagID)
if err != nil {
return nil, httperror.InternalServerError("Unable to find a tag inside the database", err)
@@ -187,7 +154,17 @@ func (handler *Handler) updateEndpointGroup(tx dataservices.DataStoreTx, endpoin
if endpoint.Type == portainer.KubernetesLocalEnvironment || endpoint.Type == portainer.AgentOnKubernetesEnvironment || endpoint.Type == portainer.EdgeAgentOnKubernetesEnvironment {
err = handler.AuthorizationService.CleanNAPWithOverridePolicies(tx, &endpoint, endpointGroup)
if err != nil {
return nil, httperror.InternalServerError("Unable to update user authorizations", err)
// Update flag with endpoint and continue
go func(endpointID portainer.EndpointID, endpointGroupID portainer.EndpointGroupID) {
err := handler.PendingActionsService.Create(portainer.PendingActions{
EndpointID: endpointID,
Action: "CleanNAPWithOverridePolicies",
ActionData: endpointGroupID,
})
if err != nil {
log.Error().Err(err).Msgf("Unable to create pending action to clean NAP with override policies for endpoint (%d) and endpoint group (%d).", endpointID, endpointGroupID)
}
}(endpoint.ID, endpointGroup.ID)
}
}
}

View File

@@ -4,18 +4,20 @@ import (
"net/http"
"github.com/portainer/portainer/api/dataservices"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/internal/authorization"
"github.com/portainer/portainer/api/pendingactions"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
"github.com/gorilla/mux"
httperror "github.com/portainer/libhttp/error"
"github.com/portainer/portainer/api/http/security"
)
// Handler is the HTTP handler used to handle environment(endpoint) group operations.
type Handler struct {
*mux.Router
AuthorizationService *authorization.Service
DataStore dataservices.DataStore
AuthorizationService *authorization.Service
DataStore dataservices.DataStore
PendingActionsService *pendingactions.PendingActionsService
}
// NewHandler creates a handler to manage environment(endpoint) group operations.

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