Compare commits

...

47 Commits

Author SHA1 Message Date
yi-portainer dbae99ea87 Merge branch 'release/2.6' 2021-07-30 11:14:07 +12:00
yi-portainer 3254051647 * update version to 2.6.2 2021-07-30 10:28:09 +12:00
yi-portainer f0d128f212 Merge branch 'release/2.6' 2021-07-29 17:37:27 +12:00
Matt Hook a0b52fc3d7 Fixes for EE-1035 and dockerhub pro accounts. (#5343) 2021-07-27 10:41:58 +12:00
cong meng 31fdef1e60 fix(advance deploy): EE-1141 A standard user can escalate to cluster administrator privileges on Kubernetes (#5324)
* fix(advance deploy): EE-1141 A standard user can escalate to cluster administrator privileges on Kubernetes

* fix(advance deploy): EE-1141 reuse existing token cache when do deployment

* fix: EE-1141 use user's SA token to exec pod command

* fix: EE-1141 stop advanced-deploy or pod-exec if user's SA token is empty

Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-07-27 09:55:09 +12:00
Hui be30e1c453 fix(swagger): add swagger annotation for pull and redeploy stack 2021-07-22 11:39:47 +12:00
Richard Wei 5b55b890e7 fix charts x label padding (#5339) 2021-07-21 13:54:26 +12:00
Dmitry Salakhov a5eac07b0c fix(namespace): update portainer-config when delete a namespace (#5328) 2021-07-20 14:05:40 +12:00
fhanportainer fa80a7b7e5 fix(k8s): fixed generating kube auction summary issue (#5332) 2021-07-19 19:45:14 +12:00
yi-portainer b14500a2d5 Merge branch 'release/2.6' 2021-07-09 16:43:09 +12:00
cong meng 278667825a EE-1110 Ingress routes and their mapping to a application name are not deleted when the application is deleted (#5291)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-07-09 10:39:14 +12:00
cong meng 65ded647b6 fix(ingress): fixed hostname field when having multiple ingresses EE-1072 (#5273) (#5285)
Co-authored-by: fhanportainer <79428273+fhanportainer@users.noreply.github.com>
2021-07-08 12:08:20 +12:00
Richard Wei 084cdcd8dc fix(app):Set resource assignment default to off EE-1043 (#5286) 2021-07-08 12:08:10 +12:00
Stéphane Busso 5b68c4365e Merge branch 'release/2.6' of github.com:portainer/portainer into release/2.6 2021-07-08 11:39:21 +12:00
Stéphane Busso 9cd64664cc fix download logs (#5243) 2021-07-08 11:37:18 +12:00
yi-portainer e831fa4a03 * update versions to 2.6.1 2021-07-07 17:20:18 +12:00
cong meng 2a3c807978 fix(ingress): EE-1049 Ingress config is lost when deleting an application deployed with ingress (#5264)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-07-07 14:08:20 +12:00
cong meng a8265a44d0 fix EE-1078 Too strict form validation for docker environment variables (#5278)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-07-07 12:52:37 +12:00
Hui 71ad21598b remove expiry time copy logic (#5259) 2021-06-30 16:49:48 +12:00
yi-portainer 6e017ea64e Merge branch 'release/2.6' 2021-06-25 00:03:04 +12:00
yi-portainer d48980e85b Merge branch 'release/2.5' 2021-05-28 10:22:50 +12:00
yi-portainer 80d3fcc40b Merge branch 'release/2.5' 2021-05-28 10:17:05 +12:00
yi-portainer 2e92706ead Merge branch 'release/2.5' 2021-05-24 08:50:46 +12:00
yi-portainer d4fa9db432 Merge branch 'release/2.5' 2021-05-17 13:59:38 +12:00
yi-portainer a28559777f Merge branch 'release/2.1' 2021-05-17 13:43:48 +12:00
yi-portainer f6531627d4 Squashed commit of the following:
commit 535215833d
Author: yi-portainer <yi.chen@portainer.io>
Date:   Thu Feb 4 18:04:18 2021 +1300

    * version change to 2.1.1

commit c4a1243af9
Author: Dmitry Salakhov <to@dimasalakhov.com>
Date:   Thu Feb 4 03:00:25 2021 +0000

    fix: docker-compose use custom config.json to access private images (#4820)

commit 305d0d2da0
Author: LP B <xAt0mZ@users.noreply.github.com>
Date:   Wed Feb 3 06:38:56 2021 +0100

    fix(k8s/resource-pool): unusable RP access management (#4810)

    (cherry picked from commit e401724d43)

commit e4605d990d
Author: yi-portainer <yi.chen@portainer.io>
Date:   Tue Feb 2 17:42:57 2021 +1300

    * update portainer version

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

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

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

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

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

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

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

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

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

    * fix missing kubectl download (#4802)

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

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

    * feat(build): introducing buildx for Windows

    * feat(build): re-ordered USER

    * feat(build): Fixed Typo

    * feat(build): fixed typo

commit a71e71f481
Author: Dmitry Salakhov <to@dimasalakhov.com>
Date:   Mon Jan 25 19:16:53 2021 +0000

    feat(compose): add docker-compose wrapper (#4713)

    * feat(compose): add docker-compose wrapper

    ce-187

    * fix(compose): pick compose implementation upon startup

    * Add static compose build for linux

    * Fix wget

    * Fix platofrm specific docker-compose download

    * Keep amd64 architecture as download parameter

    * Add tmp folder for docker-compose

    * fix: line endings

    * add proxy server

    * logs

    * Proxy

    * Add lite transport for compose

    * Fix local deployment

    * refactor: pass proxyManager by ref

    * fix: string conversion

    * refactor: compose wrapper remove unused code

    * fix: tests

    * Add edge

    * Fix merge issue

    * refactor: remove unused code

    * Move server to proxy implementation

    * Cleanup wrapper and manager

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

    * fix: pick compose syntax version

    * fix: store wrapper version in portainer

    * Get and show composeSyntaxMaxVersion at stack creation screen

    * Get and show composeSyntaxMaxVersion at stack editor screen

    * refactor: proxy server

    * Fix used tmp

    * Bump docker-compose to 1.28.0

    * remove message for docker compose limitation

    * fix: markup typo

    * Rollback docker compose to 1.27.4

    * * attempt to fix the windows build issue

    * * attempt to debug grunt issue

    * * use console log in grunt file

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

    * Remove tmp folder

    * Remove builder stage

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

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

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

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

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

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

    * refactor: renames

    * fix(stack): get endpoint by EndpointProvider

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

    Co-authored-by: Stéphane Busso <stephane.busso@gmail.com>
    Co-authored-by: Simon Meng <simon.meng@portainer.io>
    Co-authored-by: yi-portainer <yi.chen@portainer.io>
    Co-authored-by: Steven Kang <skan070@gmail.com>

commit 83f4c5ec0b
Author: LP B <xAt0mZ@users.noreply.github.com>
Date:   Mon Jan 25 02:43:54 2021 +0100

    fix(k8s/app): remove advanced deployment panel from app details view (#4730)

commit 41308d570d
Author: Maxime Bajeux <max.bajeux@gmail.com>
Date:   Mon Jan 25 02:14:35 2021 +0100

    feat(configurations): Review UI/UX configurations (#4691)

    * feat(configurations): Review UI/UX configurations

    * feat(configurations): fix binary secret value

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

    * fix(configuration): parseYaml before create configuration

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

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

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

    * fix(configuration): fix bad import in helper

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

commit 46ff8a01bc
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Fri Jan 22 03:08:08 2021 +0200

    fix(kubernetes/pods): save note (#4675)

    * feat(kubernetes/pods): introduce patch api

    * feat(k8s/pods): pod converter

    * feat(kubernetes/pods): introduce patch api

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

    * fix(k8s/pod): replace class with factory function

commit 2b257d2785
Author: yi-portainer <yi.chen@portainer.io>
Date:   Thu Jan 21 00:02:22 2021 +1300

    Squashed commit of the following 2.0.1 release fixes:

    commit f90d6b55d6
    Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
    Date:   Wed Jan 13 00:56:19 2021 +0200

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

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

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

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

        * bump the APIVersion to 2.0.1 (#4688)

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

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

        This reverts commit 380f106571.

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

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

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

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

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

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

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

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

        * fix(frontend) rephrase comments (#4629)

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

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

commit da41dbb79a
Author: cong meng <mcpacino@gmail.com>
Date:   Wed Jan 20 15:19:35 2021 +1300

    fix(stack): stacks created via API are incorrectly marked as private with no owner (#3721) (#4725)

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

commit 68d42617f2
Author: Maxime Bajeux <max.bajeux@gmail.com>
Date:   Wed Jan 20 01:02:18 2021 +0100

    feat(placement): Add a warning notification under the placement tab when an application cannot be scheduled on any node in the cluster (#4525)

    * feat(placement): Add a warning notification under the placement tab when an application cannot be scheduled on any node in the cluster

    * fix(applications): if there is at least one node the application can schedule on, then do not show the warning

commit 8323e22309
Author: Anthony McMahon <75223906+Anthony-Portainer@users.noreply.github.com>
Date:   Wed Jan 20 12:06:25 2021 +1300

    Update issue templates

    Adding auto labelling to Bug Report (kind/bug, bug/unconfirmed) and Question (kind/question)

commit 20d4341170
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Tue Jan 19 00:10:08 2021 +0200

    fix(state): check validity of state (#4609)

commit 832cafc933
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Mon Jan 18 02:59:57 2021 +0200

    fix(registries): update password only when not empty (#4669)

commit f3c537ac2c
Author: cong meng <mcpacino@gmail.com>
Date:   Mon Jan 18 13:02:16 2021 +1300

    chore(build): bump Kompose version (#4473) (#4724)

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

commit 958baf6283
Author: Anthony McMahon <75223906+Anthony-Portainer@users.noreply.github.com>
Date:   Mon Jan 18 09:30:17 2021 +1300

    Update README.md

commit 08e392378e
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Sun Jan 17 09:28:09 2021 +0200

    chore(app): fail on angular components missing nginject (#4224)

commit a2d9734b8b
Author: Alice Groux <alice.grx@gmail.com>
Date:   Sun Jan 17 04:50:22 2021 +0100

    fix(k8s/datatables): reduce size of collapse/expand column for stacks datatable and storage datatable (#4511)

    * fix(k8s/datatables): reduce size of collapse/expand column for stacks datatable and storage datatable

    * fix(k8s/datatables): reduce size of expand/collapse column

commit 15aed9fc6f
Author: DarkAEther <30438425+DarkAEther@users.noreply.github.com>
Date:   Sun Jan 17 06:23:32 2021 +0530

    feat(area/kubernetes): show shared access policy in volume details (#4707)

commit 121d33538d
Author: Alice Groux <alice.grx@gmail.com>
Date:   Fri Jan 15 02:51:36 2021 +0100

    fix(k8s/application): validate load balancer ports inputs (#4426)

    * fix(k8s/application): validate load balancer ports inputs

    * fix(k8s/application): allow user to only change the protocol on the first port mapping

commit 7a03351df8
Author: Olli Janatuinen <olljanat@users.noreply.github.com>
Date:   Thu Jan 14 23:05:33 2021 +0200

    dep(api): Support Docker Stack 3.8 (#4333)

    - Linux: Update Docker binary to version 19.03.13
    - Windows: Update Docker binary to version 19.03.12

commit 0c2987893d
Author: Alice Groux <alice.grx@gmail.com>
Date:   Thu Jan 14 03:04:44 2021 +0100

    feat(app/images): in advanced mode, remove tooltip and add an information message (#4528)

commit d1eddaa188
Author: Alice Groux <alice.grx@gmail.com>
Date:   Thu Jan 14 00:24:56 2021 +0100

    feat(app/network): rename restrict external acces to the network label and add a tooltip (#4514)

commit d336ada3c2
Author: Anthony Lapenna <anthony.lapenna@portainer.io>
Date:   Wed Jan 13 16:13:27 2021 +1300

    feat(k8s/application): review application creation warning style (#4613)

commit 839198fbff
Author: Avadhut Tanugade <30384908+mrwhoknows55@users.noreply.github.com>
Date:   Wed Jan 13 04:49:18 2021 +0530

commit 486ffa5bbd
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Tue Jan 12 23:40:09 2021 +0200

    chore(webpack): add source maps (#4471)

    * chore(webpack): add source maps

    * feat(build): fetch source maps for 3rd party libs

commit 4cd468ce21
Author: Maxime Bajeux <max.bajeux@gmail.com>
Date:   Tue Jan 12 02:35:59 2021 +0100

    Can't create kubernetes resources with a username longer than 63 characters (#4672)

    * fix(kubernetes): truncate username when we create resource

    * fix(k8s): remove forbidden characters in owner label

commit cbd7fdc62e
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Tue Jan 12 01:38:49 2021 +0200

    feat(docker/stacks): introduce date info for stacks (#4660)

    * feat(docker/stacks): add creation and update dates

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

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

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

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

    * feat(docker/stacks): toggle updated column

    * refactor(datatable): create column visibility component

    Co-authored-by: alice groux <alice.grx@gmail.com>

commit b9fe8009dd
Author: DarkAEther <30438425+DarkAEther@users.noreply.github.com>
Date:   Mon Jan 11 08:05:19 2021 +0530

    feat(image-details): Show labels in images datatable (#4287)

    * feat(images): show labels in images datatable

    * move labels to image details view

commit 6a504e7134
Author: Stéphane Busso <sbusso@users.noreply.github.com>
Date:   Mon Jan 11 14:44:15 2021 +1300

    fix(settings): Use default setting if UserSessionTimeout not set (#4521)

    * fix(settings): Use default settings if UserSessionTimeout not set

    * Update UserSessionTimeout settings in database if set to empty string

commit 51ba0876a5
Author: Alice Groux <alice.grx@gmail.com>
Date:   Mon Jan 11 00:51:46 2021 +0100

    feat(k8s/configuration): rename add ingress controller button and changed information text (#4540)

commit 769e6a4c6c
Author: Alice Groux <alice.grx@gmail.com>
Date:   Sun Jan 10 23:30:31 2021 +0100

    feat(k8s/configuration): add extra information panel when creating a sensitive configuration (#4541)

commit 105d1ae519
Author: cong meng <mcpacino@gmail.com>
Date:   Fri Jan 8 15:30:43 2021 +1300

    feat(frontend): de-emphasize internal login when OAuth is enabled (#3065) (#4565)

    * feat(frontend): de-emphasize internal login when OAuth is enabled (#3065)

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

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

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

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

commit cf508065ec
Author: cong meng <mcpacino@gmail.com>
Date:   Fri Jan 8 12:51:27 2021 +1300

    fix(frontend): application edit page initializes the overridenKeyType of new added configuration key  to NONE so that the user can select how to load it (#4548) (#4593)

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

commit eab828279e
Author: itsconquest <william.conquest@portainer.io>
Date:   Fri Jan 8 12:46:57 2021 +1300

    chore(project): exclude refactors (#4689)

commit d5763a970b
Author: cong meng <mcpacino@gmail.com>
Date:   Fri Jan 8 12:45:06 2021 +1300

    fix(frontend): Resource pool 'created' attribute is showing the time you view it at & not actual creation time (#4568) (#4599)

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

commit c9f68a4d8f
Author: cong meng <mcpacino@gmail.com>
Date:   Fri Jan 8 11:55:42 2021 +1300

    fix(kubernetes): removes kube client cache when edge proxy is removed (#4487) (#4574)

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

commit 7848bcf2f4
Author: Alice Groux <alice.grx@gmail.com>
Date:   Thu Jan 7 22:29:17 2021 +0100

    feat(k8s/resources-list-view): add advanced deployment panel to resources list view (#4516)

    * feat(k8s/resources-list-view): add advanced deployment panel to applications view, configurations view and volumes view

    * feat(k8s/resources-list-view): move advanced deployment into a template and use it everywhere

commit b924347c5b
Author: Stéphane Busso <stephane.busso@gmail.com>
Date:   Thu Jan 7 14:03:46 2021 +1300

    Bump portainer version

commit 9fbda9fb99
Author: Yi Chen <69284638+yi-portainer@users.noreply.github.com>
Date:   Thu Jan 7 13:38:01 2021 +1300

    Merge in release fixes to develop (#4687)

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

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

    * fix(frontend) rephrase comments (#4629)

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

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

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

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

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

    This reverts commit 380f106571.

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

    Co-authored-by: cong meng <mcpacino@gmail.com>
    Co-authored-by: Simon Meng <simon.meng@portainer.io>
    Co-authored-by: Stéphane Busso <sbusso@users.noreply.github.com>
    Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

commit 82f8062784
Author: Anthony Lapenna <lapenna.anthony@gmail.com>
Date:   Wed Jan 6 11:31:05 2021 +1300

    chore(github): update issue template

commit 49982eb98a
Author: knittl <knittl89+github@gmail.com>
Date:   Tue Jan 5 20:49:50 2021 +0100

commit 4be3ac470f
Merge: 7975ef79 a50ab51b
Author: Stéphane Busso <sbusso@users.noreply.github.com>
Date:   Thu Dec 24 23:45:53 2020 +1300

    Merge pull request #4658 from portainer/revert-4475-chore-ce-86-bump-kompose-version

    Revert "chore(build): bump Kompose version"

commit a50ab51bef
Author: Stéphane Busso <sbusso@users.noreply.github.com>
Date:   Thu Dec 24 12:12:28 2020 +1300

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

    This reverts commit 380f106571.
2021-02-04 18:08:27 +13:00
yi-portainer 535215833d * version change to 2.1.1 2021-02-04 18:04:18 +13:00
yi-portainer 666b09ad3b Squashed commit of the following:
commit c4a1243af9
Author: Dmitry Salakhov <to@dimasalakhov.com>
Date:   Thu Feb 4 03:00:25 2021 +0000

    fix: docker-compose use custom config.json to access private images (#4820)

commit 305d0d2da0
Author: LP B <xAt0mZ@users.noreply.github.com>
Date:   Wed Feb 3 06:38:56 2021 +0100

    fix(k8s/resource-pool): unusable RP access management (#4810)

    (cherry picked from commit e401724d43)

commit e4605d990d
Author: yi-portainer <yi.chen@portainer.io>
Date:   Tue Feb 2 17:42:57 2021 +1300

    * update portainer version

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

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

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

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

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

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

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

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

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

    * fix missing kubectl download (#4802)

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

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

    * feat(build): introducing buildx for Windows

    * feat(build): re-ordered USER

    * feat(build): Fixed Typo

    * feat(build): fixed typo

commit a71e71f481
Author: Dmitry Salakhov <to@dimasalakhov.com>
Date:   Mon Jan 25 19:16:53 2021 +0000

    feat(compose): add docker-compose wrapper (#4713)

    * feat(compose): add docker-compose wrapper

    ce-187

    * fix(compose): pick compose implementation upon startup

    * Add static compose build for linux

    * Fix wget

    * Fix platofrm specific docker-compose download

    * Keep amd64 architecture as download parameter

    * Add tmp folder for docker-compose

    * fix: line endings

    * add proxy server

    * logs

    * Proxy

    * Add lite transport for compose

    * Fix local deployment

    * refactor: pass proxyManager by ref

    * fix: string conversion

    * refactor: compose wrapper remove unused code

    * fix: tests

    * Add edge

    * Fix merge issue

    * refactor: remove unused code

    * Move server to proxy implementation

    * Cleanup wrapper and manager

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

    * fix: pick compose syntax version

    * fix: store wrapper version in portainer

    * Get and show composeSyntaxMaxVersion at stack creation screen

    * Get and show composeSyntaxMaxVersion at stack editor screen

    * refactor: proxy server

    * Fix used tmp

    * Bump docker-compose to 1.28.0

    * remove message for docker compose limitation

    * fix: markup typo

    * Rollback docker compose to 1.27.4

    * * attempt to fix the windows build issue

    * * attempt to debug grunt issue

    * * use console log in grunt file

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

    * Remove tmp folder

    * Remove builder stage

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

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

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

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

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

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

    * refactor: renames

    * fix(stack): get endpoint by EndpointProvider

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

    Co-authored-by: Stéphane Busso <stephane.busso@gmail.com>
    Co-authored-by: Simon Meng <simon.meng@portainer.io>
    Co-authored-by: yi-portainer <yi.chen@portainer.io>
    Co-authored-by: Steven Kang <skan070@gmail.com>

commit 83f4c5ec0b
Author: LP B <xAt0mZ@users.noreply.github.com>
Date:   Mon Jan 25 02:43:54 2021 +0100

    fix(k8s/app): remove advanced deployment panel from app details view (#4730)

commit 41308d570d
Author: Maxime Bajeux <max.bajeux@gmail.com>
Date:   Mon Jan 25 02:14:35 2021 +0100

    feat(configurations): Review UI/UX configurations (#4691)

    * feat(configurations): Review UI/UX configurations

    * feat(configurations): fix binary secret value

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

    * fix(configuration): parseYaml before create configuration

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

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

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

    * fix(configuration): fix bad import in helper

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

commit 46ff8a01bc
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Fri Jan 22 03:08:08 2021 +0200

    fix(kubernetes/pods): save note (#4675)

    * feat(kubernetes/pods): introduce patch api

    * feat(k8s/pods): pod converter

    * feat(kubernetes/pods): introduce patch api

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

    * fix(k8s/pod): replace class with factory function

commit 2b257d2785
Author: yi-portainer <yi.chen@portainer.io>
Date:   Thu Jan 21 00:02:22 2021 +1300

    Squashed commit of the following 2.0.1 release fixes:

    commit f90d6b55d6
    Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
    Date:   Wed Jan 13 00:56:19 2021 +0200

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

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

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

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

        * bump the APIVersion to 2.0.1 (#4688)

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

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

        This reverts commit 380f106571.

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

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

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

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

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

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

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

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

        * fix(frontend) rephrase comments (#4629)

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

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

commit da41dbb79a
Author: cong meng <mcpacino@gmail.com>
Date:   Wed Jan 20 15:19:35 2021 +1300

    fix(stack): stacks created via API are incorrectly marked as private with no owner (#3721) (#4725)

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

commit 68d42617f2
Author: Maxime Bajeux <max.bajeux@gmail.com>
Date:   Wed Jan 20 01:02:18 2021 +0100

    feat(placement): Add a warning notification under the placement tab when an application cannot be scheduled on any node in the cluster (#4525)

    * feat(placement): Add a warning notification under the placement tab when an application cannot be scheduled on any node in the cluster

    * fix(applications): if there is at least one node the application can schedule on, then do not show the warning

commit 8323e22309
Author: Anthony McMahon <75223906+Anthony-Portainer@users.noreply.github.com>
Date:   Wed Jan 20 12:06:25 2021 +1300

    Update issue templates

    Adding auto labelling to Bug Report (kind/bug, bug/unconfirmed) and Question (kind/question)

commit 20d4341170
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Tue Jan 19 00:10:08 2021 +0200

    fix(state): check validity of state (#4609)

commit 832cafc933
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Mon Jan 18 02:59:57 2021 +0200

    fix(registries): update password only when not empty (#4669)

commit f3c537ac2c
Author: cong meng <mcpacino@gmail.com>
Date:   Mon Jan 18 13:02:16 2021 +1300

    chore(build): bump Kompose version (#4473) (#4724)

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

commit 958baf6283
Author: Anthony McMahon <75223906+Anthony-Portainer@users.noreply.github.com>
Date:   Mon Jan 18 09:30:17 2021 +1300

    Update README.md

commit 08e392378e
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Sun Jan 17 09:28:09 2021 +0200

    chore(app): fail on angular components missing nginject (#4224)

commit a2d9734b8b
Author: Alice Groux <alice.grx@gmail.com>
Date:   Sun Jan 17 04:50:22 2021 +0100

    fix(k8s/datatables): reduce size of collapse/expand column for stacks datatable and storage datatable (#4511)

    * fix(k8s/datatables): reduce size of collapse/expand column for stacks datatable and storage datatable

    * fix(k8s/datatables): reduce size of expand/collapse column

commit 15aed9fc6f
Author: DarkAEther <30438425+DarkAEther@users.noreply.github.com>
Date:   Sun Jan 17 06:23:32 2021 +0530

    feat(area/kubernetes): show shared access policy in volume details (#4707)

commit 121d33538d
Author: Alice Groux <alice.grx@gmail.com>
Date:   Fri Jan 15 02:51:36 2021 +0100

    fix(k8s/application): validate load balancer ports inputs (#4426)

    * fix(k8s/application): validate load balancer ports inputs

    * fix(k8s/application): allow user to only change the protocol on the first port mapping

commit 7a03351df8
Author: Olli Janatuinen <olljanat@users.noreply.github.com>
Date:   Thu Jan 14 23:05:33 2021 +0200

    dep(api): Support Docker Stack 3.8 (#4333)

    - Linux: Update Docker binary to version 19.03.13
    - Windows: Update Docker binary to version 19.03.12

commit 0c2987893d
Author: Alice Groux <alice.grx@gmail.com>
Date:   Thu Jan 14 03:04:44 2021 +0100

    feat(app/images): in advanced mode, remove tooltip and add an information message (#4528)

commit d1eddaa188
Author: Alice Groux <alice.grx@gmail.com>
Date:   Thu Jan 14 00:24:56 2021 +0100

    feat(app/network): rename restrict external acces to the network label and add a tooltip (#4514)

commit d336ada3c2
Author: Anthony Lapenna <anthony.lapenna@portainer.io>
Date:   Wed Jan 13 16:13:27 2021 +1300

    feat(k8s/application): review application creation warning style (#4613)

commit 839198fbff
Author: Avadhut Tanugade <30384908+mrwhoknows55@users.noreply.github.com>
Date:   Wed Jan 13 04:49:18 2021 +0530

commit 486ffa5bbd
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Tue Jan 12 23:40:09 2021 +0200

    chore(webpack): add source maps (#4471)

    * chore(webpack): add source maps

    * feat(build): fetch source maps for 3rd party libs

commit 4cd468ce21
Author: Maxime Bajeux <max.bajeux@gmail.com>
Date:   Tue Jan 12 02:35:59 2021 +0100

    Can't create kubernetes resources with a username longer than 63 characters (#4672)

    * fix(kubernetes): truncate username when we create resource

    * fix(k8s): remove forbidden characters in owner label

commit cbd7fdc62e
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Tue Jan 12 01:38:49 2021 +0200

    feat(docker/stacks): introduce date info for stacks (#4660)

    * feat(docker/stacks): add creation and update dates

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

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

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

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

    * feat(docker/stacks): toggle updated column

    * refactor(datatable): create column visibility component

    Co-authored-by: alice groux <alice.grx@gmail.com>

commit b9fe8009dd
Author: DarkAEther <30438425+DarkAEther@users.noreply.github.com>
Date:   Mon Jan 11 08:05:19 2021 +0530

    feat(image-details): Show labels in images datatable (#4287)

    * feat(images): show labels in images datatable

    * move labels to image details view

commit 6a504e7134
Author: Stéphane Busso <sbusso@users.noreply.github.com>
Date:   Mon Jan 11 14:44:15 2021 +1300

    fix(settings): Use default setting if UserSessionTimeout not set (#4521)

    * fix(settings): Use default settings if UserSessionTimeout not set

    * Update UserSessionTimeout settings in database if set to empty string

commit 51ba0876a5
Author: Alice Groux <alice.grx@gmail.com>
Date:   Mon Jan 11 00:51:46 2021 +0100

    feat(k8s/configuration): rename add ingress controller button and changed information text (#4540)

commit 769e6a4c6c
Author: Alice Groux <alice.grx@gmail.com>
Date:   Sun Jan 10 23:30:31 2021 +0100

    feat(k8s/configuration): add extra information panel when creating a sensitive configuration (#4541)

commit 105d1ae519
Author: cong meng <mcpacino@gmail.com>
Date:   Fri Jan 8 15:30:43 2021 +1300

    feat(frontend): de-emphasize internal login when OAuth is enabled (#3065) (#4565)

    * feat(frontend): de-emphasize internal login when OAuth is enabled (#3065)

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

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

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

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

commit cf508065ec
Author: cong meng <mcpacino@gmail.com>
Date:   Fri Jan 8 12:51:27 2021 +1300

    fix(frontend): application edit page initializes the overridenKeyType of new added configuration key  to NONE so that the user can select how to load it (#4548) (#4593)

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

commit eab828279e
Author: itsconquest <william.conquest@portainer.io>
Date:   Fri Jan 8 12:46:57 2021 +1300

    chore(project): exclude refactors (#4689)

commit d5763a970b
Author: cong meng <mcpacino@gmail.com>
Date:   Fri Jan 8 12:45:06 2021 +1300

    fix(frontend): Resource pool 'created' attribute is showing the time you view it at & not actual creation time (#4568) (#4599)

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

commit c9f68a4d8f
Author: cong meng <mcpacino@gmail.com>
Date:   Fri Jan 8 11:55:42 2021 +1300

    fix(kubernetes): removes kube client cache when edge proxy is removed (#4487) (#4574)

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

commit 7848bcf2f4
Author: Alice Groux <alice.grx@gmail.com>
Date:   Thu Jan 7 22:29:17 2021 +0100

    feat(k8s/resources-list-view): add advanced deployment panel to resources list view (#4516)

    * feat(k8s/resources-list-view): add advanced deployment panel to applications view, configurations view and volumes view

    * feat(k8s/resources-list-view): move advanced deployment into a template and use it everywhere

commit b924347c5b
Author: Stéphane Busso <stephane.busso@gmail.com>
Date:   Thu Jan 7 14:03:46 2021 +1300

    Bump portainer version

commit 9fbda9fb99
Author: Yi Chen <69284638+yi-portainer@users.noreply.github.com>
Date:   Thu Jan 7 13:38:01 2021 +1300

    Merge in release fixes to develop (#4687)

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

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

    * fix(frontend) rephrase comments (#4629)

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

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

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

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

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

    This reverts commit 380f106571.

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

    Co-authored-by: cong meng <mcpacino@gmail.com>
    Co-authored-by: Simon Meng <simon.meng@portainer.io>
    Co-authored-by: Stéphane Busso <sbusso@users.noreply.github.com>
    Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

commit 82f8062784
Author: Anthony Lapenna <lapenna.anthony@gmail.com>
Date:   Wed Jan 6 11:31:05 2021 +1300

    chore(github): update issue template

commit 49982eb98a
Author: knittl <knittl89+github@gmail.com>
Date:   Tue Jan 5 20:49:50 2021 +0100

commit 4be3ac470f
Merge: 7975ef79 a50ab51b
Author: Stéphane Busso <sbusso@users.noreply.github.com>
Date:   Thu Dec 24 23:45:53 2020 +1300

    Merge pull request #4658 from portainer/revert-4475-chore-ce-86-bump-kompose-version

    Revert "chore(build): bump Kompose version"

commit a50ab51bef
Author: Stéphane Busso <sbusso@users.noreply.github.com>
Date:   Thu Dec 24 12:12:28 2020 +1300

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

    This reverts commit 380f106571.
2021-02-04 17:28:23 +13:00
Dmitry Salakhov c4a1243af9 fix: docker-compose use custom config.json to access private images (#4820) 2021-02-04 16:00:25 +13:00
LP B 305d0d2da0 fix(k8s/resource-pool): unusable RP access management (#4810)
(cherry picked from commit e401724d43)
2021-02-04 15:58:32 +13:00
yi-portainer 9af9b70f3e Squashed commit of the following:
commit e4605d990d
Author: yi-portainer <yi.chen@portainer.io>
Date:   Tue Feb 2 17:42:57 2021 +1300

    * update portainer version

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

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

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

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

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

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

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

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

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

    * fix missing kubectl download (#4802)

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

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

    * feat(build): introducing buildx for Windows

    * feat(build): re-ordered USER

    * feat(build): Fixed Typo

    * feat(build): fixed typo

commit a71e71f481
Author: Dmitry Salakhov <to@dimasalakhov.com>
Date:   Mon Jan 25 19:16:53 2021 +0000

    feat(compose): add docker-compose wrapper (#4713)

    * feat(compose): add docker-compose wrapper

    ce-187

    * fix(compose): pick compose implementation upon startup

    * Add static compose build for linux

    * Fix wget

    * Fix platofrm specific docker-compose download

    * Keep amd64 architecture as download parameter

    * Add tmp folder for docker-compose

    * fix: line endings

    * add proxy server

    * logs

    * Proxy

    * Add lite transport for compose

    * Fix local deployment

    * refactor: pass proxyManager by ref

    * fix: string conversion

    * refactor: compose wrapper remove unused code

    * fix: tests

    * Add edge

    * Fix merge issue

    * refactor: remove unused code

    * Move server to proxy implementation

    * Cleanup wrapper and manager

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

    * fix: pick compose syntax version

    * fix: store wrapper version in portainer

    * Get and show composeSyntaxMaxVersion at stack creation screen

    * Get and show composeSyntaxMaxVersion at stack editor screen

    * refactor: proxy server

    * Fix used tmp

    * Bump docker-compose to 1.28.0

    * remove message for docker compose limitation

    * fix: markup typo

    * Rollback docker compose to 1.27.4

    * * attempt to fix the windows build issue

    * * attempt to debug grunt issue

    * * use console log in grunt file

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

    * Remove tmp folder

    * Remove builder stage

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

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

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

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

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

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

    * refactor: renames

    * fix(stack): get endpoint by EndpointProvider

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

    Co-authored-by: Stéphane Busso <stephane.busso@gmail.com>
    Co-authored-by: Simon Meng <simon.meng@portainer.io>
    Co-authored-by: yi-portainer <yi.chen@portainer.io>
    Co-authored-by: Steven Kang <skan070@gmail.com>

commit 83f4c5ec0b
Author: LP B <xAt0mZ@users.noreply.github.com>
Date:   Mon Jan 25 02:43:54 2021 +0100

    fix(k8s/app): remove advanced deployment panel from app details view (#4730)

commit 41308d570d
Author: Maxime Bajeux <max.bajeux@gmail.com>
Date:   Mon Jan 25 02:14:35 2021 +0100

    feat(configurations): Review UI/UX configurations (#4691)

    * feat(configurations): Review UI/UX configurations

    * feat(configurations): fix binary secret value

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

    * fix(configuration): parseYaml before create configuration

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

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

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

    * fix(configuration): fix bad import in helper

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

commit 46ff8a01bc
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Fri Jan 22 03:08:08 2021 +0200

    fix(kubernetes/pods): save note (#4675)

    * feat(kubernetes/pods): introduce patch api

    * feat(k8s/pods): pod converter

    * feat(kubernetes/pods): introduce patch api

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

    * fix(k8s/pod): replace class with factory function

commit 2b257d2785
Author: yi-portainer <yi.chen@portainer.io>
Date:   Thu Jan 21 00:02:22 2021 +1300

    Squashed commit of the following 2.0.1 release fixes:

    commit f90d6b55d6
    Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
    Date:   Wed Jan 13 00:56:19 2021 +0200

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

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

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

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

        * bump the APIVersion to 2.0.1 (#4688)

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

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

        This reverts commit 380f106571.

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

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

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

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

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

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

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

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

        * fix(frontend) rephrase comments (#4629)

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

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

commit da41dbb79a
Author: cong meng <mcpacino@gmail.com>
Date:   Wed Jan 20 15:19:35 2021 +1300

    fix(stack): stacks created via API are incorrectly marked as private with no owner (#3721) (#4725)

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

commit 68d42617f2
Author: Maxime Bajeux <max.bajeux@gmail.com>
Date:   Wed Jan 20 01:02:18 2021 +0100

    feat(placement): Add a warning notification under the placement tab when an application cannot be scheduled on any node in the cluster (#4525)

    * feat(placement): Add a warning notification under the placement tab when an application cannot be scheduled on any node in the cluster

    * fix(applications): if there is at least one node the application can schedule on, then do not show the warning

commit 8323e22309
Author: Anthony McMahon <75223906+Anthony-Portainer@users.noreply.github.com>
Date:   Wed Jan 20 12:06:25 2021 +1300

    Update issue templates

    Adding auto labelling to Bug Report (kind/bug, bug/unconfirmed) and Question (kind/question)

commit 20d4341170
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Tue Jan 19 00:10:08 2021 +0200

    fix(state): check validity of state (#4609)

commit 832cafc933
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Mon Jan 18 02:59:57 2021 +0200

    fix(registries): update password only when not empty (#4669)

commit f3c537ac2c
Author: cong meng <mcpacino@gmail.com>
Date:   Mon Jan 18 13:02:16 2021 +1300

    chore(build): bump Kompose version (#4473) (#4724)

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

commit 958baf6283
Author: Anthony McMahon <75223906+Anthony-Portainer@users.noreply.github.com>
Date:   Mon Jan 18 09:30:17 2021 +1300

    Update README.md

commit 08e392378e
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Sun Jan 17 09:28:09 2021 +0200

    chore(app): fail on angular components missing nginject (#4224)

commit a2d9734b8b
Author: Alice Groux <alice.grx@gmail.com>
Date:   Sun Jan 17 04:50:22 2021 +0100

    fix(k8s/datatables): reduce size of collapse/expand column for stacks datatable and storage datatable (#4511)

    * fix(k8s/datatables): reduce size of collapse/expand column for stacks datatable and storage datatable

    * fix(k8s/datatables): reduce size of expand/collapse column

commit 15aed9fc6f
Author: DarkAEther <30438425+DarkAEther@users.noreply.github.com>
Date:   Sun Jan 17 06:23:32 2021 +0530

    feat(area/kubernetes): show shared access policy in volume details (#4707)

commit 121d33538d
Author: Alice Groux <alice.grx@gmail.com>
Date:   Fri Jan 15 02:51:36 2021 +0100

    fix(k8s/application): validate load balancer ports inputs (#4426)

    * fix(k8s/application): validate load balancer ports inputs

    * fix(k8s/application): allow user to only change the protocol on the first port mapping

commit 7a03351df8
Author: Olli Janatuinen <olljanat@users.noreply.github.com>
Date:   Thu Jan 14 23:05:33 2021 +0200

    dep(api): Support Docker Stack 3.8 (#4333)

    - Linux: Update Docker binary to version 19.03.13
    - Windows: Update Docker binary to version 19.03.12

commit 0c2987893d
Author: Alice Groux <alice.grx@gmail.com>
Date:   Thu Jan 14 03:04:44 2021 +0100

    feat(app/images): in advanced mode, remove tooltip and add an information message (#4528)

commit d1eddaa188
Author: Alice Groux <alice.grx@gmail.com>
Date:   Thu Jan 14 00:24:56 2021 +0100

    feat(app/network): rename restrict external acces to the network label and add a tooltip (#4514)

commit d336ada3c2
Author: Anthony Lapenna <anthony.lapenna@portainer.io>
Date:   Wed Jan 13 16:13:27 2021 +1300

    feat(k8s/application): review application creation warning style (#4613)

commit 839198fbff
Author: Avadhut Tanugade <30384908+mrwhoknows55@users.noreply.github.com>
Date:   Wed Jan 13 04:49:18 2021 +0530

commit 486ffa5bbd
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Tue Jan 12 23:40:09 2021 +0200

    chore(webpack): add source maps (#4471)

    * chore(webpack): add source maps

    * feat(build): fetch source maps for 3rd party libs

commit 4cd468ce21
Author: Maxime Bajeux <max.bajeux@gmail.com>
Date:   Tue Jan 12 02:35:59 2021 +0100

    Can't create kubernetes resources with a username longer than 63 characters (#4672)

    * fix(kubernetes): truncate username when we create resource

    * fix(k8s): remove forbidden characters in owner label

commit cbd7fdc62e
Author: Chaim Lev-Ari <chiptus@users.noreply.github.com>
Date:   Tue Jan 12 01:38:49 2021 +0200

    feat(docker/stacks): introduce date info for stacks (#4660)

    * feat(docker/stacks): add creation and update dates

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

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

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

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

    * feat(docker/stacks): toggle updated column

    * refactor(datatable): create column visibility component

    Co-authored-by: alice groux <alice.grx@gmail.com>

commit b9fe8009dd
Author: DarkAEther <30438425+DarkAEther@users.noreply.github.com>
Date:   Mon Jan 11 08:05:19 2021 +0530

    feat(image-details): Show labels in images datatable (#4287)

    * feat(images): show labels in images datatable

    * move labels to image details view

commit 6a504e7134
Author: Stéphane Busso <sbusso@users.noreply.github.com>
Date:   Mon Jan 11 14:44:15 2021 +1300

    fix(settings): Use default setting if UserSessionTimeout not set (#4521)

    * fix(settings): Use default settings if UserSessionTimeout not set

    * Update UserSessionTimeout settings in database if set to empty string

commit 51ba0876a5
Author: Alice Groux <alice.grx@gmail.com>
Date:   Mon Jan 11 00:51:46 2021 +0100

    feat(k8s/configuration): rename add ingress controller button and changed information text (#4540)

commit 769e6a4c6c
Author: Alice Groux <alice.grx@gmail.com>
Date:   Sun Jan 10 23:30:31 2021 +0100

    feat(k8s/configuration): add extra information panel when creating a sensitive configuration (#4541)

commit 105d1ae519
Author: cong meng <mcpacino@gmail.com>
Date:   Fri Jan 8 15:30:43 2021 +1300

    feat(frontend): de-emphasize internal login when OAuth is enabled (#3065) (#4565)

    * feat(frontend): de-emphasize internal login when OAuth is enabled (#3065)

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

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

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

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

commit cf508065ec
Author: cong meng <mcpacino@gmail.com>
Date:   Fri Jan 8 12:51:27 2021 +1300

    fix(frontend): application edit page initializes the overridenKeyType of new added configuration key  to NONE so that the user can select how to load it (#4548) (#4593)

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

commit eab828279e
Author: itsconquest <william.conquest@portainer.io>
Date:   Fri Jan 8 12:46:57 2021 +1300

    chore(project): exclude refactors (#4689)

commit d5763a970b
Author: cong meng <mcpacino@gmail.com>
Date:   Fri Jan 8 12:45:06 2021 +1300

    fix(frontend): Resource pool 'created' attribute is showing the time you view it at & not actual creation time (#4568) (#4599)

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

commit c9f68a4d8f
Author: cong meng <mcpacino@gmail.com>
Date:   Fri Jan 8 11:55:42 2021 +1300

    fix(kubernetes): removes kube client cache when edge proxy is removed (#4487) (#4574)

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

commit 7848bcf2f4
Author: Alice Groux <alice.grx@gmail.com>
Date:   Thu Jan 7 22:29:17 2021 +0100

    feat(k8s/resources-list-view): add advanced deployment panel to resources list view (#4516)

    * feat(k8s/resources-list-view): add advanced deployment panel to applications view, configurations view and volumes view

    * feat(k8s/resources-list-view): move advanced deployment into a template and use it everywhere

commit b924347c5b
Author: Stéphane Busso <stephane.busso@gmail.com>
Date:   Thu Jan 7 14:03:46 2021 +1300

    Bump portainer version

commit 9fbda9fb99
Author: Yi Chen <69284638+yi-portainer@users.noreply.github.com>
Date:   Thu Jan 7 13:38:01 2021 +1300

    Merge in release fixes to develop (#4687)

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

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

    * fix(frontend) rephrase comments (#4629)

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

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

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

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

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

    This reverts commit 380f106571.

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

    Co-authored-by: cong meng <mcpacino@gmail.com>
    Co-authored-by: Simon Meng <simon.meng@portainer.io>
    Co-authored-by: Stéphane Busso <sbusso@users.noreply.github.com>
    Co-authored-by: Anthony Lapenna <anthony.lapenna@portainer.io>

commit 82f8062784
Author: Anthony Lapenna <lapenna.anthony@gmail.com>
Date:   Wed Jan 6 11:31:05 2021 +1300

    chore(github): update issue template

commit 49982eb98a
Author: knittl <knittl89+github@gmail.com>
Date:   Tue Jan 5 20:49:50 2021 +0100

commit 4be3ac470f
Merge: 7975ef79 a50ab51b
Author: Stéphane Busso <sbusso@users.noreply.github.com>
Date:   Thu Dec 24 23:45:53 2020 +1300

    Merge pull request #4658 from portainer/revert-4475-chore-ce-86-bump-kompose-version

    Revert "chore(build): bump Kompose version"

commit a50ab51bef
Author: Stéphane Busso <sbusso@users.noreply.github.com>
Date:   Thu Dec 24 12:12:28 2020 +1300

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

    This reverts commit 380f106571.
2021-02-02 17:54:02 +13:00
yi-portainer e4605d990d * update portainer version 2021-02-02 17:42:57 +13:00
LP B 768697157c sec(app): remove unused and vulnerable dependencies (#4801) 2021-02-02 17:02:06 +13:00
cong meng d3086da139 fix(k8s) trigger port validation while changing protocol (ce#394) (#4804)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-02-02 15:10:06 +13:00
cong meng 95894e8047 fix(k8s) parse empty configuration as empty string yaml instead of {} (ce#395) (#4805)
Co-authored-by: Simon Meng <simon.meng@portainer.io>
2021-02-02 15:03:11 +13:00
Yi Chen 81de55fedd * fix missing kubectl download (#4802) 2021-02-02 11:12:40 +13:00
Steven Kang 84827b8782 feat(build): introducing buildx for Windows (#4792)
* feat(build): introducing buildx for Windows

* feat(build): re-ordered USER

* feat(build): Fixed Typo

* feat(build): fixed typo
2021-01-31 17:32:30 +13:00
yi-portainer fa38af5d81 Merge remote-tracking branch 'origin/release/2.0.1' 2021-01-07 14:56:52 +13:00
Yi Chen 1b82b450d7 * bump the APIVersion to 2.0.1 (#4688) 2021-01-07 14:47:32 +13:00
Yi Chen b78d804881 Revert "chore(build): bump Kompose version (#4475)" (#4676)
This reverts commit 380f106571.

Co-authored-by: Stéphane Busso <sbusso@users.noreply.github.com>
2020-12-30 23:03:43 +13:00
Anthony Lapenna 51b72c12f9 fix(docker/stack-details): do not display editor tab for external stack (#4650) 2020-12-23 14:45:32 +13:00
Yi Chen 58c04bdbe3 + silently continue when downloading artifacts in windows (#4637) 2020-12-22 13:47:11 +13:00
cong meng a6320d5222 fix(frontend) unable to retrieve config map error when trying to manage newly created resource pool (ce#180) (#4618)
* fix(frontend) unable to retrieve config map error when trying to manage newly created resource pool (ce#180)

* fix(frontend) rephrase comments (#4629)

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

Co-authored-by: Simon Meng <simon.meng@portainer.io>
Co-authored-by: Stéphane Busso <sbusso@users.noreply.github.com>
2020-12-22 13:38:54 +13:00
Anthony Lapenna cb4b4a43e6 update pull dog configuration 2020-08-31 18:09:19 +12:00
Anthony Lapenna 1e5a1d5bdd Merge branch 'develop' 2020-08-31 18:06:50 +12:00
Anthony Lapenna 5ed0d21c39 Merge branch 'ee-pulldog' 2020-08-28 15:26:30 +12:00
Anthony Lapenna 2972dbeafb feat(build/pulldog): review pulldog configuration 2020-08-18 12:36:01 +12:00
50 changed files with 554 additions and 233 deletions
-1
View File
@@ -4,7 +4,6 @@ about: Create a bug report
title: '' title: ''
labels: bug/need-confirmation, kind/bug labels: bug/need-confirmation, kind/bug
assignees: '' assignees: ''
--- ---
<!-- <!--
+1 -1
View File
@@ -4,8 +4,8 @@ about: Ask us a question about Portainer usage or deployment
title: '' title: ''
labels: '' labels: ''
assignees: '' assignees: ''
--- ---
Before you start, we need a little bit more information from you: Before you start, we need a little bit more information from you:
Use Case (delete as appropriate): Using Portainer at Home, Using Portainer in a Commerical setup. Use Case (delete as appropriate): Using Portainer at Home, Using Portainer in a Commerical setup.
+3 -3
View File
@@ -89,8 +89,8 @@ func initSwarmStackManager(assetsPath string, dataStorePath string, signatureSer
return exec.NewSwarmStackManager(assetsPath, dataStorePath, signatureService, fileService, reverseTunnelService) return exec.NewSwarmStackManager(assetsPath, dataStorePath, signatureService, fileService, reverseTunnelService)
} }
func initKubernetesDeployer(dataStore portainer.DataStore, reverseTunnelService portainer.ReverseTunnelService, signatureService portainer.DigitalSignatureService, assetsPath string) portainer.KubernetesDeployer { func initKubernetesDeployer(kubernetesTokenCacheManager *kubeproxy.TokenCacheManager, kubernetesClientFactory *kubecli.ClientFactory, dataStore portainer.DataStore, reverseTunnelService portainer.ReverseTunnelService, signatureService portainer.DigitalSignatureService, assetsPath string) portainer.KubernetesDeployer {
return exec.NewKubernetesDeployer(dataStore, reverseTunnelService, signatureService, assetsPath) return exec.NewKubernetesDeployer(kubernetesTokenCacheManager, kubernetesClientFactory, dataStore, reverseTunnelService, signatureService, assetsPath)
} }
func initJWTService(dataStore portainer.DataStore) (portainer.JWTService, error) { func initJWTService(dataStore portainer.DataStore) (portainer.JWTService, error) {
@@ -402,7 +402,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
composeStackManager := initComposeStackManager(*flags.Assets, *flags.Data, reverseTunnelService, proxyManager) composeStackManager := initComposeStackManager(*flags.Assets, *flags.Data, reverseTunnelService, proxyManager)
kubernetesDeployer := initKubernetesDeployer(dataStore, reverseTunnelService, digitalSignatureService, *flags.Assets) kubernetesDeployer := initKubernetesDeployer(kubernetesTokenCacheManager, kubernetesClientFactory, dataStore, reverseTunnelService, digitalSignatureService, *flags.Assets)
if dataStore.IsNew() { if dataStore.IsNew() {
err = updateSettingsFromFlags(dataStore, flags) err = updateSettingsFromFlags(dataStore, flags)
+58 -12
View File
@@ -5,6 +5,9 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"github.com/portainer/portainer/api/http/proxy/factory/kubernetes"
"github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/kubernetes/cli"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
@@ -20,27 +23,64 @@ import (
// KubernetesDeployer represents a service to deploy resources inside a Kubernetes environment. // KubernetesDeployer represents a service to deploy resources inside a Kubernetes environment.
type KubernetesDeployer struct { type KubernetesDeployer struct {
binaryPath string binaryPath string
dataStore portainer.DataStore dataStore portainer.DataStore
reverseTunnelService portainer.ReverseTunnelService reverseTunnelService portainer.ReverseTunnelService
signatureService portainer.DigitalSignatureService signatureService portainer.DigitalSignatureService
kubernetesClientFactory *cli.ClientFactory
kubernetesTokenCacheManager *kubernetes.TokenCacheManager
} }
// NewKubernetesDeployer initializes a new KubernetesDeployer service. // NewKubernetesDeployer initializes a new KubernetesDeployer service.
func NewKubernetesDeployer(datastore portainer.DataStore, reverseTunnelService portainer.ReverseTunnelService, signatureService portainer.DigitalSignatureService, binaryPath string) *KubernetesDeployer { func NewKubernetesDeployer(kubernetesTokenCacheManager *kubernetes.TokenCacheManager, kubernetesClientFactory *cli.ClientFactory, datastore portainer.DataStore, reverseTunnelService portainer.ReverseTunnelService, signatureService portainer.DigitalSignatureService, binaryPath string) *KubernetesDeployer {
return &KubernetesDeployer{ return &KubernetesDeployer{
binaryPath: binaryPath, binaryPath: binaryPath,
dataStore: datastore, dataStore: datastore,
reverseTunnelService: reverseTunnelService, reverseTunnelService: reverseTunnelService,
signatureService: signatureService, signatureService: signatureService,
kubernetesClientFactory: kubernetesClientFactory,
kubernetesTokenCacheManager: kubernetesTokenCacheManager,
} }
} }
func (deployer *KubernetesDeployer) getToken(request *http.Request, endpoint *portainer.Endpoint, setLocalAdminToken bool) (string, error) {
tokenData, err := security.RetrieveTokenData(request)
if err != nil {
return "", err
}
kubecli, err := deployer.kubernetesClientFactory.GetKubeClient(endpoint)
if err != nil {
return "", err
}
tokenCache := deployer.kubernetesTokenCacheManager.GetOrCreateTokenCache(int(endpoint.ID))
tokenManager, err := kubernetes.NewTokenManager(kubecli, deployer.dataStore, tokenCache, setLocalAdminToken)
if err != nil {
return "", err
}
if tokenData.Role == portainer.AdministratorRole {
return tokenManager.GetAdminServiceAccountToken(), nil
}
token, err := tokenManager.GetUserServiceAccountToken(int(tokenData.ID))
if err != nil {
return "", err
}
if token == "" {
return "", fmt.Errorf("can not get a valid user service account token")
}
return token, nil
}
// Deploy will deploy a Kubernetes manifest inside a specific namespace in a Kubernetes endpoint. // Deploy will deploy a Kubernetes manifest inside a specific namespace in a Kubernetes endpoint.
// Otherwise it will use kubectl to deploy the manifest. // Otherwise it will use kubectl to deploy the manifest.
func (deployer *KubernetesDeployer) Deploy(endpoint *portainer.Endpoint, stackConfig string, namespace string) (string, error) { func (deployer *KubernetesDeployer) Deploy(request *http.Request, endpoint *portainer.Endpoint, stackConfig string, namespace string) (string, error) {
if endpoint.Type == portainer.KubernetesLocalEnvironment { if endpoint.Type == portainer.KubernetesLocalEnvironment {
token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token") token, err := deployer.getToken(request, endpoint, true);
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -53,7 +93,7 @@ func (deployer *KubernetesDeployer) Deploy(endpoint *portainer.Endpoint, stackCo
args := make([]string, 0) args := make([]string, 0)
args = append(args, "--server", endpoint.URL) args = append(args, "--server", endpoint.URL)
args = append(args, "--insecure-skip-tls-verify") args = append(args, "--insecure-skip-tls-verify")
args = append(args, "--token", string(token)) args = append(args, "--token", token)
args = append(args, "--namespace", namespace) args = append(args, "--namespace", namespace)
args = append(args, "apply", "-f", "-") args = append(args, "apply", "-f", "-")
@@ -139,8 +179,14 @@ func (deployer *KubernetesDeployer) Deploy(endpoint *portainer.Endpoint, stackCo
return "", err return "", err
} }
token, err := deployer.getToken(request, endpoint, false);
if err != nil {
return "", err
}
req.Header.Set(portainer.PortainerAgentPublicKeyHeader, deployer.signatureService.EncodedPublicKey()) req.Header.Set(portainer.PortainerAgentPublicKeyHeader, deployer.signatureService.EncodedPublicKey())
req.Header.Set(portainer.PortainerAgentSignatureHeader, signature) req.Header.Set(portainer.PortainerAgentSignatureHeader, signature)
req.Header.Set(portainer.PortainerAgentKubernetesSATokenHeader, token)
resp, err := httpCli.Do(req) resp, err := httpCli.Do(req)
if err != nil { if err != nil {
+6 -3
View File
@@ -1,7 +1,10 @@
package gittypes package gittypes
type RepoConfig struct { type RepoConfig struct {
URL string // The repo url
ReferenceName string URL string `example:"https://github.com/portainer/portainer-ee.git"`
ConfigFilePath string // The reference name
ReferenceName string `example:"refs/heads/branch_name"`
// Path to where the config file is in this url/refName
ConfigFilePath string `example:"docker-compose.yml"`
} }
+7
View File
@@ -80,6 +80,7 @@ github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkg
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
@@ -153,6 +154,7 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
@@ -219,8 +221,10 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420 h1:Yu3681ykYHDfLoI6XVjL4JWmkE+3TX9yfIWwRCh1kFM= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420 h1:Yu3681ykYHDfLoI6XVjL4JWmkE+3TX9yfIWwRCh1kFM=
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
@@ -366,9 +370,11 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
@@ -395,6 +401,7 @@ k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUc
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
-9
View File
@@ -5,7 +5,6 @@ import (
"log" "log"
"net/http" "net/http"
"strings" "strings"
"time"
"github.com/asaskevich/govalidator" "github.com/asaskevich/govalidator"
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
@@ -134,14 +133,6 @@ func (handler *Handler) writeToken(w http.ResponseWriter, user *portainer.User)
return handler.persistAndWriteToken(w, composeTokenData(user)) return handler.persistAndWriteToken(w, composeTokenData(user))
} }
func (handler *Handler) writeTokenForOAuth(w http.ResponseWriter, user *portainer.User, expiryTime *time.Time) *httperror.HandlerError {
token, err := handler.JWTService.GenerateTokenForOAuth(composeTokenData(user), expiryTime)
if err != nil {
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to generate JWT token", Err: err}
}
return response.JSON(w, &authenticateResponse{JWT: token})
}
func (handler *Handler) persistAndWriteToken(w http.ResponseWriter, tokenData *portainer.TokenData) *httperror.HandlerError { func (handler *Handler) persistAndWriteToken(w http.ResponseWriter, tokenData *portainer.TokenData) *httperror.HandlerError {
token, err := handler.JWTService.GenerateToken(tokenData) token, err := handler.JWTService.GenerateToken(tokenData)
if err != nil { if err != nil {
+8 -9
View File
@@ -4,7 +4,6 @@ import (
"errors" "errors"
"log" "log"
"net/http" "net/http"
"time"
"github.com/asaskevich/govalidator" "github.com/asaskevich/govalidator"
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
@@ -26,21 +25,21 @@ func (payload *oauthPayload) Validate(r *http.Request) error {
return nil return nil
} }
func (handler *Handler) authenticateOAuth(code string, settings *portainer.OAuthSettings) (string, *time.Time, error) { func (handler *Handler) authenticateOAuth(code string, settings *portainer.OAuthSettings) (string, error) {
if code == "" { if code == "" {
return "", nil, errors.New("Invalid OAuth authorization code") return "", errors.New("Invalid OAuth authorization code")
} }
if settings == nil { if settings == nil {
return "", nil, errors.New("Invalid OAuth configuration") return "", errors.New("Invalid OAuth configuration")
} }
username, expiryTime, err := handler.OAuthService.Authenticate(code, settings) username, err := handler.OAuthService.Authenticate(code, settings)
if err != nil { if err != nil {
return "", nil, err return "", err
} }
return username, expiryTime, nil return username, nil
} }
// @id ValidateOAuth // @id ValidateOAuth
@@ -70,7 +69,7 @@ func (handler *Handler) validateOAuth(w http.ResponseWriter, r *http.Request) *h
return &httperror.HandlerError{StatusCode: http.StatusForbidden, Message: "OAuth authentication is not enabled", Err: errors.New("OAuth authentication is not enabled")} return &httperror.HandlerError{StatusCode: http.StatusForbidden, Message: "OAuth authentication is not enabled", Err: errors.New("OAuth authentication is not enabled")}
} }
username, expiryTime, err := handler.authenticateOAuth(payload.Code, &settings.OAuthSettings) username, err := handler.authenticateOAuth(payload.Code, &settings.OAuthSettings)
if err != nil { if err != nil {
log.Printf("[DEBUG] - OAuth authentication error: %s", err) log.Printf("[DEBUG] - OAuth authentication error: %s", err)
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to authenticate through OAuth", Err: httperrors.ErrUnauthorized} return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to authenticate through OAuth", Err: httperrors.ErrUnauthorized}
@@ -111,5 +110,5 @@ func (handler *Handler) validateOAuth(w http.ResponseWriter, r *http.Request) *h
} }
return handler.writeTokenForOAuth(w, user, expiryTime) return handler.writeToken(w, user)
} }
@@ -115,8 +115,18 @@ func getDockerHubLimits(httpClient *client.HTTPClient, token string) (*dockerhub
return nil, errors.New("failed fetching dockerhub limits") return nil, errors.New("failed fetching dockerhub limits")
} }
// An error with rateLimit-Limit or RateLimit-Remaining is likely for dockerhub pro accounts where there is no rate limit.
// In that specific case the headers will not be present. Don't bubble up the error as its normal
// See: https://docs.docker.com/docker-hub/download-rate-limit/
rateLimit, err := parseRateLimitHeader(resp.Header, "RateLimit-Limit") rateLimit, err := parseRateLimitHeader(resp.Header, "RateLimit-Limit")
if err != nil {
return nil, nil
}
rateLimitRemaining, err := parseRateLimitHeader(resp.Header, "RateLimit-Remaining") rateLimitRemaining, err := parseRateLimitHeader(resp.Header, "RateLimit-Remaining")
if err != nil {
return nil, nil
}
return &dockerhubStatusResponse{ return &dockerhubStatusResponse{
Limit: rateLimit, Limit: rateLimit,
+1 -1
View File
@@ -67,7 +67,7 @@ type Handler struct {
} }
// @title PortainerCE API // @title PortainerCE API
// @version 2.1.1 // @version 2.6.2
// @description.markdown api-description.md // @description.markdown api-description.md
// @termsOfService // @termsOfService
@@ -95,7 +95,7 @@ func (handler *Handler) createKubernetesStackFromFileContent(w http.ResponseWrit
doCleanUp := true doCleanUp := true
defer handler.cleanUp(stack, &doCleanUp) defer handler.cleanUp(stack, &doCleanUp)
output, err := handler.deployKubernetesStack(endpoint, payload.StackFileContent, payload.ComposeFormat, payload.Namespace) output, err := handler.deployKubernetesStack(r, endpoint, payload.StackFileContent, payload.ComposeFormat, payload.Namespace)
if err != nil { if err != nil {
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to deploy Kubernetes stack", Err: err} return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to deploy Kubernetes stack", Err: err}
} }
@@ -139,7 +139,7 @@ func (handler *Handler) createKubernetesStackFromGitRepository(w http.ResponseWr
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Failed to process manifest from Git repository", Err: err} return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Failed to process manifest from Git repository", Err: err}
} }
output, err := handler.deployKubernetesStack(endpoint, stackFileContent, payload.ComposeFormat, payload.Namespace) output, err := handler.deployKubernetesStack(r, endpoint, stackFileContent, payload.ComposeFormat, payload.Namespace)
if err != nil { if err != nil {
return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to deploy Kubernetes stack", Err: err} return &httperror.HandlerError{StatusCode: http.StatusInternalServerError, Message: "Unable to deploy Kubernetes stack", Err: err}
} }
@@ -155,7 +155,7 @@ func (handler *Handler) createKubernetesStackFromGitRepository(w http.ResponseWr
return response.JSON(w, resp) return response.JSON(w, resp)
} }
func (handler *Handler) deployKubernetesStack(endpoint *portainer.Endpoint, stackConfig string, composeFormat bool, namespace string) (string, error) { func (handler *Handler) deployKubernetesStack(request *http.Request, endpoint *portainer.Endpoint, stackConfig string, composeFormat bool, namespace string) (string, error) {
handler.stackCreationMutex.Lock() handler.stackCreationMutex.Lock()
defer handler.stackCreationMutex.Unlock() defer handler.stackCreationMutex.Unlock()
@@ -167,7 +167,7 @@ func (handler *Handler) deployKubernetesStack(endpoint *portainer.Endpoint, stac
stackConfig = string(convertedConfig) stackConfig = string(convertedConfig)
} }
return handler.KubernetesDeployer.Deploy(endpoint, stackConfig, namespace) return handler.KubernetesDeployer.Deploy(request, endpoint, stackConfig, namespace)
} }
+1 -1
View File
@@ -27,7 +27,7 @@ import (
// @success 204 "Success" // @success 204 "Success"
// @failure 400 "Invalid request" // @failure 400 "Invalid request"
// @failure 403 "Permission denied" // @failure 403 "Permission denied"
// @failure 404 " not found" // @failure 404 "Not found"
// @failure 500 "Server error" // @failure 500 "Server error"
// @router /stacks/{id} [delete] // @router /stacks/{id} [delete]
func (handler *Handler) stackDelete(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { func (handler *Handler) stackDelete(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
+1 -1
View File
@@ -26,7 +26,7 @@ import (
// @success 200 {object} portainer.Stack "Success" // @success 200 {object} portainer.Stack "Success"
// @failure 400 "Invalid request" // @failure 400 "Invalid request"
// @failure 403 "Permission denied" // @failure 403 "Permission denied"
// @failure 404 " not found" // @failure 404 "Not found"
// @failure 500 "Server error" // @failure 500 "Server error"
// @router /stacks/{id}/start [post] // @router /stacks/{id}/start [post]
func (handler *Handler) stackStart(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { func (handler *Handler) stackStart(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
+1 -1
View File
@@ -24,7 +24,7 @@ import (
// @success 200 {object} portainer.Stack "Success" // @success 200 {object} portainer.Stack "Success"
// @failure 400 "Invalid request" // @failure 400 "Invalid request"
// @failure 403 "Permission denied" // @failure 403 "Permission denied"
// @failure 404 " not found" // @failure 404 "Not found"
// @failure 500 "Server error" // @failure 500 "Server error"
// @router /stacks/{id}/stop [post] // @router /stacks/{id}/stop [post]
func (handler *Handler) stackStop(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { func (handler *Handler) stackStop(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
+17 -1
View File
@@ -33,7 +33,23 @@ func (payload *updateStackGitPayload) Validate(r *http.Request) error {
return nil return nil
} }
// PUT request on /api/stacks/:id/git?endpointId=<endpointId> // @id StackUpdateGit
// @summary Redeploy a stack
// @description Pull and redeploy a stack via Git
// @description **Access policy**: restricted
// @tags stacks
// @security jwt
// @accept json
// @produce json
// @param id path int true "Stack identifier"
// @param endpointId query int false "Stacks created before version 1.18.0 might not have an associated endpoint identifier. Use this optional parameter to set the endpoint identifier used by the stack."
// @param body body updateStackGitPayload true "Git configs for pull and redeploy a stack"
// @success 200 {object} portainer.Stack "Success"
// @failure 400 "Invalid request"
// @failure 403 "Permission denied"
// @failure 404 "Not found"
// @failure 500 "Server error"
// @router /stacks/{id}/git [put]
func (handler *Handler) stackUpdateGit(w http.ResponseWriter, r *http.Request) *httperror.HandlerError { func (handler *Handler) stackUpdateGit(w http.ResponseWriter, r *http.Request) *httperror.HandlerError {
stackID, err := request.RetrieveNumericRouteVariableValue(r, "id") stackID, err := request.RetrieveNumericRouteVariableValue(r, "id")
if err != nil { if err != nil {
+10 -7
View File
@@ -5,6 +5,7 @@ import (
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
httperror "github.com/portainer/libhttp/error" httperror "github.com/portainer/libhttp/error"
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/http/proxy/factory/kubernetes"
"github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/http/security"
"github.com/portainer/portainer/api/kubernetes/cli" "github.com/portainer/portainer/api/kubernetes/cli"
) )
@@ -12,20 +13,22 @@ import (
// Handler is the HTTP handler used to handle websocket operations. // Handler is the HTTP handler used to handle websocket operations.
type Handler struct { type Handler struct {
*mux.Router *mux.Router
DataStore portainer.DataStore DataStore portainer.DataStore
SignatureService portainer.DigitalSignatureService SignatureService portainer.DigitalSignatureService
ReverseTunnelService portainer.ReverseTunnelService ReverseTunnelService portainer.ReverseTunnelService
KubernetesClientFactory *cli.ClientFactory KubernetesClientFactory *cli.ClientFactory
requestBouncer *security.RequestBouncer requestBouncer *security.RequestBouncer
connectionUpgrader websocket.Upgrader connectionUpgrader websocket.Upgrader
kubernetesTokenCacheManager *kubernetes.TokenCacheManager
} }
// NewHandler creates a handler to manage websocket operations. // NewHandler creates a handler to manage websocket operations.
func NewHandler(bouncer *security.RequestBouncer) *Handler { func NewHandler(kubernetesTokenCacheManager *kubernetes.TokenCacheManager, bouncer *security.RequestBouncer) *Handler {
h := &Handler{ h := &Handler{
Router: mux.NewRouter(), Router: mux.NewRouter(),
connectionUpgrader: websocket.Upgrader{}, connectionUpgrader: websocket.Upgrader{},
requestBouncer: bouncer, requestBouncer: bouncer,
kubernetesTokenCacheManager: kubernetesTokenCacheManager,
} }
h.PathPrefix("/websocket/exec").Handler( h.PathPrefix("/websocket/exec").Handler(
bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.websocketExec))) bouncer.AuthenticatedAccess(httperror.LoggerHandler(h.websocketExec)))
+44 -1
View File
@@ -1,6 +1,8 @@
package websocket package websocket
import ( import (
"fmt"
"github.com/portainer/portainer/api/http/security"
"io" "io"
"log" "log"
"net/http" "net/http"
@@ -11,6 +13,7 @@ import (
"github.com/portainer/libhttp/request" "github.com/portainer/libhttp/request"
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
bolterrors "github.com/portainer/portainer/api/bolt/errors" bolterrors "github.com/portainer/portainer/api/bolt/errors"
"github.com/portainer/portainer/api/http/proxy/factory/kubernetes"
) )
// @summary Execute a websocket on pod // @summary Execute a websocket on pod
@@ -70,8 +73,14 @@ func (handler *Handler) websocketPodExec(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access endpoint", err} return &httperror.HandlerError{http.StatusForbidden, "Permission denied to access endpoint", err}
} }
token, useAdminToken, err := handler.getToken(r, endpoint, false)
if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to get user service account token", err}
}
params := &webSocketRequestParams{ params := &webSocketRequestParams{
endpoint: endpoint, endpoint: endpoint,
token: token,
} }
r.Header.Del("Origin") r.Header.Del("Origin")
@@ -112,7 +121,7 @@ func (handler *Handler) websocketPodExec(w http.ResponseWriter, r *http.Request)
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to create Kubernetes client", err} return &httperror.HandlerError{http.StatusInternalServerError, "Unable to create Kubernetes client", err}
} }
err = cli.StartExecProcess(namespace, podName, containerName, commandArray, stdinReader, stdoutWriter) err = cli.StartExecProcess(token, useAdminToken, namespace, podName, containerName, commandArray, stdinReader, stdoutWriter)
if err != nil { if err != nil {
return &httperror.HandlerError{http.StatusInternalServerError, "Unable to start exec process inside container", err} return &httperror.HandlerError{http.StatusInternalServerError, "Unable to start exec process inside container", err}
} }
@@ -124,3 +133,37 @@ func (handler *Handler) websocketPodExec(w http.ResponseWriter, r *http.Request)
return nil return nil
} }
func (handler *Handler) getToken(request *http.Request, endpoint *portainer.Endpoint, setLocalAdminToken bool) (string, bool, error) {
tokenData, err := security.RetrieveTokenData(request)
if err != nil {
return "", false, err
}
kubecli, err := handler.KubernetesClientFactory.GetKubeClient(endpoint)
if err != nil {
return "", false, err
}
tokenCache := handler.kubernetesTokenCacheManager.GetOrCreateTokenCache(int(endpoint.ID))
tokenManager, err := kubernetes.NewTokenManager(kubecli, handler.DataStore, tokenCache, setLocalAdminToken)
if err != nil {
return "", false, err
}
if tokenData.Role == portainer.AdministratorRole {
return tokenManager.GetAdminServiceAccountToken(), true, nil
}
token, err := tokenManager.GetUserServiceAccountToken(int(tokenData.ID))
if err != nil {
return "", false, err
}
if token == "" {
return "", false, fmt.Errorf("can not get a valid user service account token")
}
return token, false, nil
}
+2
View File
@@ -24,6 +24,7 @@ func (handler *Handler) proxyEdgeAgentWebsocketRequest(w http.ResponseWriter, r
proxy.Director = func(incoming *http.Request, out http.Header) { proxy.Director = func(incoming *http.Request, out http.Header) {
out.Set(portainer.PortainerAgentTargetHeader, params.nodeName) out.Set(portainer.PortainerAgentTargetHeader, params.nodeName)
out.Set(portainer.PortainerAgentKubernetesSATokenHeader, params.token)
} }
handler.ReverseTunnelService.SetTunnelStatusToActive(params.endpoint.ID) handler.ReverseTunnelService.SetTunnelStatusToActive(params.endpoint.ID)
@@ -64,6 +65,7 @@ func (handler *Handler) proxyAgentWebsocketRequest(w http.ResponseWriter, r *htt
out.Set(portainer.PortainerAgentPublicKeyHeader, handler.SignatureService.EncodedPublicKey()) out.Set(portainer.PortainerAgentPublicKeyHeader, handler.SignatureService.EncodedPublicKey())
out.Set(portainer.PortainerAgentSignatureHeader, signature) out.Set(portainer.PortainerAgentSignatureHeader, signature)
out.Set(portainer.PortainerAgentTargetHeader, params.nodeName) out.Set(portainer.PortainerAgentTargetHeader, params.nodeName)
out.Set(portainer.PortainerAgentKubernetesSATokenHeader, params.token)
} }
proxy.ServeHTTP(w, r) proxy.ServeHTTP(w, r)
+1
View File
@@ -8,4 +8,5 @@ type webSocketRequestParams struct {
ID string ID string
nodeName string nodeName string
endpoint *portainer.Endpoint endpoint *portainer.Endpoint
token string
} }
+3 -3
View File
@@ -39,7 +39,7 @@ func (factory *ProxyFactory) newKubernetesLocalProxy(endpoint *portainer.Endpoin
return nil, err return nil, err
} }
transport, err := kubernetes.NewLocalTransport(tokenManager) transport, err := kubernetes.NewLocalTransport(tokenManager, endpoint, factory.dataStore)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -72,7 +72,7 @@ func (factory *ProxyFactory) newKubernetesEdgeHTTPProxy(endpoint *portainer.Endp
endpointURL.Scheme = "http" endpointURL.Scheme = "http"
proxy := newSingleHostReverseProxyWithHostHeader(endpointURL) proxy := newSingleHostReverseProxyWithHostHeader(endpointURL)
proxy.Transport = kubernetes.NewEdgeTransport(factory.dataStore, factory.reverseTunnelService, endpoint.ID, tokenManager) proxy.Transport = kubernetes.NewEdgeTransport(factory.dataStore, factory.reverseTunnelService, endpoint, tokenManager)
return proxy, nil return proxy, nil
} }
@@ -103,7 +103,7 @@ func (factory *ProxyFactory) newKubernetesAgentHTTPSProxy(endpoint *portainer.En
} }
proxy := newSingleHostReverseProxyWithHostHeader(remoteURL) proxy := newSingleHostReverseProxyWithHostHeader(remoteURL)
proxy.Transport = kubernetes.NewAgentTransport(factory.dataStore, factory.signatureService, tlsConfig, tokenManager) proxy.Transport = kubernetes.NewAgentTransport(factory.dataStore, factory.signatureService, tlsConfig, tokenManager, endpoint)
return proxy, nil return proxy, nil
} }
@@ -0,0 +1,55 @@
package kubernetes
import (
"crypto/tls"
"net/http"
"strings"
portainer "github.com/portainer/portainer/api"
)
type agentTransport struct {
*baseTransport
signatureService portainer.DigitalSignatureService
}
// NewAgentTransport returns a new transport that can be used to send signed requests to a Portainer agent
func NewAgentTransport(dataStore portainer.DataStore, signatureService portainer.DigitalSignatureService, tlsConfig *tls.Config, tokenManager *tokenManager, endpoint *portainer.Endpoint) *agentTransport {
transport := &agentTransport{
signatureService: signatureService,
baseTransport: newBaseTransport(
&http.Transport{
TLSClientConfig: tlsConfig,
},
tokenManager,
endpoint,
dataStore,
),
}
return transport
}
// RoundTrip is the implementation of the the http.RoundTripper interface
func (transport *agentTransport) RoundTrip(request *http.Request) (*http.Response, error) {
token, err := getRoundTripToken(request, transport.tokenManager, transport.endpoint.ID)
if err != nil {
return nil, err
}
request.Header.Set(portainer.PortainerAgentKubernetesSATokenHeader, token)
if strings.HasPrefix(request.URL.Path, "/v2") {
decorateAgentRequest(request, transport.dataStore)
}
signature, err := transport.signatureService.CreateSignature(portainer.PortainerAgentSignatureMessage)
if err != nil {
return nil, err
}
request.Header.Set(portainer.PortainerAgentPublicKeyHeader, transport.signatureService.EncodedPublicKey())
request.Header.Set(portainer.PortainerAgentSignatureHeader, signature)
return transport.baseTransport.RoundTrip(request)
}
@@ -0,0 +1,52 @@
package kubernetes
import (
"net/http"
"strings"
portainer "github.com/portainer/portainer/api"
)
type edgeTransport struct {
*baseTransport
reverseTunnelService portainer.ReverseTunnelService
}
// NewAgentTransport returns a new transport that can be used to send signed requests to a Portainer Edge agent
func NewEdgeTransport(dataStore portainer.DataStore, reverseTunnelService portainer.ReverseTunnelService, endpoint *portainer.Endpoint, tokenManager *tokenManager) *edgeTransport {
transport := &edgeTransport{
reverseTunnelService: reverseTunnelService,
baseTransport: newBaseTransport(
&http.Transport{},
tokenManager,
endpoint,
dataStore,
),
}
return transport
}
// RoundTrip is the implementation of the the http.RoundTripper interface
func (transport *edgeTransport) RoundTrip(request *http.Request) (*http.Response, error) {
token, err := getRoundTripToken(request, transport.tokenManager, transport.endpoint.ID)
if err != nil {
return nil, err
}
request.Header.Set(portainer.PortainerAgentKubernetesSATokenHeader, token)
if strings.HasPrefix(request.URL.Path, "/v2") {
decorateAgentRequest(request, transport.dataStore)
}
response, err := transport.baseTransport.RoundTrip(request)
if err == nil {
transport.reverseTunnelService.SetTunnelStatusToActive(transport.endpoint.ID)
} else {
transport.reverseTunnelService.SetTunnelStatusToIdle(transport.endpoint.ID)
}
return response, err
}
@@ -0,0 +1,46 @@
package kubernetes
import (
"fmt"
"net/http"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/crypto"
)
type localTransport struct {
*baseTransport
}
// NewLocalTransport returns a new transport that can be used to send requests to the local Kubernetes API
func NewLocalTransport(tokenManager *tokenManager, endpoint *portainer.Endpoint, dataStore portainer.DataStore) (*localTransport, error) {
config, err := crypto.CreateTLSConfigurationFromBytes(nil, nil, nil, true, true)
if err != nil {
return nil, err
}
transport := &localTransport{
baseTransport: newBaseTransport(
&http.Transport{
TLSClientConfig: config,
},
tokenManager,
endpoint,
dataStore,
),
}
return transport, nil
}
// RoundTrip is the implementation of the the http.RoundTripper interface
func (transport *localTransport) RoundTrip(request *http.Request) (*http.Response, error) {
token, err := getRoundTripToken(request, transport.tokenManager, transport.endpoint.ID)
if err != nil {
return nil, err
}
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
return transport.baseTransport.RoundTrip(request)
}
@@ -0,0 +1,15 @@
package kubernetes
import (
"net/http"
"github.com/pkg/errors"
)
func (transport *baseTransport) deleteNamespaceRequest(request *http.Request, namespace string) (*http.Response, error) {
if err := transport.tokenManager.kubecli.NamespaceAccessPoliciesDeleteNamespace(namespace); err != nil {
return nil, errors.WithMessagef(err, "failed to delete a namespace [%s] from portainer config", namespace)
}
return transport.executeKubernetesRequest(request, true)
}
+5 -9
View File
@@ -1,10 +1,8 @@
package kubernetes package kubernetes
import ( import (
"io/ioutil"
"sync"
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
"io/ioutil"
) )
const defaultServiceAccountTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token" const defaultServiceAccountTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
@@ -13,7 +11,6 @@ type tokenManager struct {
tokenCache *tokenCache tokenCache *tokenCache
kubecli portainer.KubeClient kubecli portainer.KubeClient
dataStore portainer.DataStore dataStore portainer.DataStore
mutex sync.Mutex
adminToken string adminToken string
} }
@@ -25,7 +22,6 @@ func NewTokenManager(kubecli portainer.KubeClient, dataStore portainer.DataStore
tokenCache: cache, tokenCache: cache,
kubecli: kubecli, kubecli: kubecli,
dataStore: dataStore, dataStore: dataStore,
mutex: sync.Mutex{},
adminToken: "", adminToken: "",
} }
@@ -41,13 +37,13 @@ func NewTokenManager(kubecli portainer.KubeClient, dataStore portainer.DataStore
return tokenManager, nil return tokenManager, nil
} }
func (manager *tokenManager) getAdminServiceAccountToken() string { func (manager *tokenManager) GetAdminServiceAccountToken() string {
return manager.adminToken return manager.adminToken
} }
func (manager *tokenManager) getUserServiceAccountToken(userID int) (string, error) { func (manager *tokenManager) GetUserServiceAccountToken(userID int) (string, error) {
manager.mutex.Lock() manager.tokenCache.mutex.Lock()
defer manager.mutex.Unlock() defer manager.tokenCache.mutex.Unlock()
token, ok := manager.tokenCache.getToken(userID) token, ok := manager.tokenCache.getToken(userID)
if !ok { if !ok {
@@ -2,6 +2,7 @@ package kubernetes
import ( import (
"strconv" "strconv"
"sync"
"github.com/orcaman/concurrent-map" "github.com/orcaman/concurrent-map"
) )
@@ -14,6 +15,7 @@ type (
tokenCache struct { tokenCache struct {
userTokenCache cmap.ConcurrentMap userTokenCache cmap.ConcurrentMap
mutex sync.Mutex
} }
) )
@@ -35,6 +37,18 @@ func (manager *TokenCacheManager) CreateTokenCache(endpointID int) *tokenCache {
return tokenCache return tokenCache
} }
// GetOrCreateTokenCache will get the tokenCache from the manager map of caches if it exists,
// otherwise it will create a new tokenCache object, associate it to the manager map of caches
// and return a pointer to that tokenCache instance.
func (manager *TokenCacheManager) GetOrCreateTokenCache(endpointID int) *tokenCache {
key := strconv.Itoa(endpointID)
if epCache, ok := manager.tokenCaches.Get(key); ok {
return epCache.(*tokenCache)
}
return manager.CreateTokenCache(endpointID)
}
// RemoveUserFromCache will ensure that the specific userID is removed from all registered caches. // RemoveUserFromCache will ensure that the specific userID is removed from all registered caches.
func (manager *TokenCacheManager) RemoveUserFromCache(userID int) { func (manager *TokenCacheManager) RemoveUserFromCache(userID int) {
for cache := range manager.tokenCaches.IterBuffered() { for cache := range manager.tokenCaches.IterBuffered() {
@@ -45,6 +59,7 @@ func (manager *TokenCacheManager) RemoveUserFromCache(userID int) {
func newTokenCache() *tokenCache { func newTokenCache() *tokenCache {
return &tokenCache{ return &tokenCache{
userTokenCache: cmap.New(), userTokenCache: cmap.New(),
mutex: sync.Mutex{},
} }
} }
+50 -124
View File
@@ -2,147 +2,73 @@ package kubernetes
import ( import (
"bytes" "bytes"
"crypto/tls"
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"log" "log"
"net/http" "net/http"
"regexp"
"strings" "strings"
"github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/http/security"
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/crypto"
) )
type ( type baseTransport struct {
localTransport struct { httpTransport *http.Transport
httpTransport *http.Transport tokenManager *tokenManager
tokenManager *tokenManager endpoint *portainer.Endpoint
endpointIdentifier portainer.EndpointID dataStore portainer.DataStore
}
agentTransport struct {
dataStore portainer.DataStore
httpTransport *http.Transport
tokenManager *tokenManager
signatureService portainer.DigitalSignatureService
endpointIdentifier portainer.EndpointID
}
edgeTransport struct {
dataStore portainer.DataStore
httpTransport *http.Transport
tokenManager *tokenManager
reverseTunnelService portainer.ReverseTunnelService
endpointIdentifier portainer.EndpointID
}
)
// NewLocalTransport returns a new transport that can be used to send requests to the local Kubernetes API
func NewLocalTransport(tokenManager *tokenManager) (*localTransport, error) {
config, err := crypto.CreateTLSConfigurationFromBytes(nil, nil, nil, true, true)
if err != nil {
return nil, err
}
transport := &localTransport{
httpTransport: &http.Transport{
TLSClientConfig: config,
},
tokenManager: tokenManager,
}
return transport, nil
} }
// RoundTrip is the implementation of the the http.RoundTripper interface func newBaseTransport(httpTransport *http.Transport, tokenManager *tokenManager, endpoint *portainer.Endpoint, dataStore portainer.DataStore) *baseTransport {
func (transport *localTransport) RoundTrip(request *http.Request) (*http.Response, error) { return &baseTransport{
token, err := getRoundTripToken(request, transport.tokenManager, transport.endpointIdentifier) httpTransport: httpTransport,
if err != nil { tokenManager: tokenManager,
return nil, err endpoint: endpoint,
dataStore: dataStore,
}
}
func (transport *baseTransport) RoundTrip(request *http.Request) (*http.Response, error) {
apiVersionRe := regexp.MustCompile(`^(/kubernetes)?/api/v[0-9](\.[0-9])?`)
requestPath := apiVersionRe.ReplaceAllString(request.URL.Path, "")
switch {
case strings.EqualFold(requestPath, "/namespaces"):
return transport.executeKubernetesRequest(request, true)
case strings.HasPrefix(requestPath, "/namespaces"):
return transport.proxyNamespacedRequest(request, requestPath)
default:
return transport.executeKubernetesRequest(request, true)
}
}
func (transport *baseTransport) proxyNamespacedRequest(request *http.Request, fullRequestPath string) (*http.Response, error) {
requestPath := strings.TrimPrefix(fullRequestPath, "/namespaces/")
split := strings.SplitN(requestPath, "/", 2)
namespace := split[0]
requestPath = ""
if len(split) > 1 {
requestPath = split[1]
} }
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) switch {
case requestPath == "" && request.Method == "DELETE":
return transport.deleteNamespaceRequest(request, namespace)
default:
return transport.executeKubernetesRequest(request, true)
}
}
func (transport *baseTransport) executeKubernetesRequest(request *http.Request, shouldLog bool) (*http.Response, error) {
return transport.httpTransport.RoundTrip(request) return transport.httpTransport.RoundTrip(request)
} }
// NewAgentTransport returns a new transport that can be used to send signed requests to a Portainer agent var (
func NewAgentTransport(datastore portainer.DataStore, signatureService portainer.DigitalSignatureService, tlsConfig *tls.Config, tokenManager *tokenManager) *agentTransport { namespaceRegex = regexp.MustCompile(`^/namespaces/([^/]*)$`)
transport := &agentTransport{ )
dataStore: datastore,
httpTransport: &http.Transport{
TLSClientConfig: tlsConfig,
},
tokenManager: tokenManager,
signatureService: signatureService,
}
return transport
}
// RoundTrip is the implementation of the the http.RoundTripper interface
func (transport *agentTransport) RoundTrip(request *http.Request) (*http.Response, error) {
token, err := getRoundTripToken(request, transport.tokenManager, transport.endpointIdentifier)
if err != nil {
return nil, err
}
request.Header.Set(portainer.PortainerAgentKubernetesSATokenHeader, token)
if strings.HasPrefix(request.URL.Path, "/v2") {
decorateAgentRequest(request, transport.dataStore)
}
signature, err := transport.signatureService.CreateSignature(portainer.PortainerAgentSignatureMessage)
if err != nil {
return nil, err
}
request.Header.Set(portainer.PortainerAgentPublicKeyHeader, transport.signatureService.EncodedPublicKey())
request.Header.Set(portainer.PortainerAgentSignatureHeader, signature)
return transport.httpTransport.RoundTrip(request)
}
// NewEdgeTransport returns a new transport that can be used to send signed requests to a Portainer Edge agent
func NewEdgeTransport(datastore portainer.DataStore, reverseTunnelService portainer.ReverseTunnelService, endpointIdentifier portainer.EndpointID, tokenManager *tokenManager) *edgeTransport {
transport := &edgeTransport{
dataStore: datastore,
httpTransport: &http.Transport{},
tokenManager: tokenManager,
reverseTunnelService: reverseTunnelService,
endpointIdentifier: endpointIdentifier,
}
return transport
}
// RoundTrip is the implementation of the the http.RoundTripper interface
func (transport *edgeTransport) RoundTrip(request *http.Request) (*http.Response, error) {
token, err := getRoundTripToken(request, transport.tokenManager, transport.endpointIdentifier)
if err != nil {
return nil, err
}
request.Header.Set(portainer.PortainerAgentKubernetesSATokenHeader, token)
if strings.HasPrefix(request.URL.Path, "/v2") {
decorateAgentRequest(request, transport.dataStore)
}
response, err := transport.httpTransport.RoundTrip(request)
if err == nil {
transport.reverseTunnelService.SetTunnelStatusToActive(transport.endpointIdentifier)
} else {
transport.reverseTunnelService.SetTunnelStatusToIdle(transport.endpointIdentifier)
}
return response, err
}
func getRoundTripToken( func getRoundTripToken(
request *http.Request, request *http.Request,
@@ -156,9 +82,9 @@ func getRoundTripToken(
var token string var token string
if tokenData.Role == portainer.AdministratorRole { if tokenData.Role == portainer.AdministratorRole {
token = tokenManager.getAdminServiceAccountToken() token = tokenManager.GetAdminServiceAccountToken()
} else { } else {
token, err = tokenManager.getUserServiceAccountToken(int(tokenData.ID)) token, err = tokenManager.GetUserServiceAccountToken(int(tokenData.ID))
if err != nil { if err != nil {
log.Printf("Failed retrieving service account token: %v", err) log.Printf("Failed retrieving service account token: %v", err)
return "", err return "", err
+1 -1
View File
@@ -204,7 +204,7 @@ func (server *Server) Start() error {
userHandler.DataStore = server.DataStore userHandler.DataStore = server.DataStore
userHandler.CryptoService = server.CryptoService userHandler.CryptoService = server.CryptoService
var websocketHandler = websocket.NewHandler(requestBouncer) var websocketHandler = websocket.NewHandler(server.KubernetesTokenCacheManager, requestBouncer)
websocketHandler.DataStore = server.DataStore websocketHandler.DataStore = server.DataStore
websocketHandler.SignatureService = server.SignatureService websocketHandler.SignatureService = server.SignatureService
websocketHandler.ReverseTunnelService = server.ReverseTunnelService websocketHandler.ReverseTunnelService = server.ReverseTunnelService
+16
View File
@@ -3,6 +3,7 @@ package cli
import ( import (
"encoding/json" "encoding/json"
"github.com/pkg/errors"
portainer "github.com/portainer/portainer/api" portainer "github.com/portainer/portainer/api"
k8serrors "k8s.io/apimachinery/pkg/api/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -80,6 +81,21 @@ func hasUserAccessToNamespace(userID int, teamIDs []int, policies portainer.K8sN
return false return false
} }
// NamespaceAccessPoliciesDeleteNamespace removes stored policies associated with a given namespace
func (kcl *KubeClient) NamespaceAccessPoliciesDeleteNamespace(ns string) error {
kcl.lock.Lock()
defer kcl.lock.Unlock()
policies, err := kcl.GetNamespaceAccessPolicies()
if err != nil {
return errors.WithMessage(err, "failed to fetch access policies")
}
delete(policies, ns)
return kcl.UpdateNamespaceAccessPolicies(policies)
}
// GetNamespaceAccessPolicies gets the namespace access policies // GetNamespaceAccessPolicies gets the namespace access policies
// from config maps in the portainer namespace // from config maps in the portainer namespace
func (kcl *KubeClient) GetNamespaceAccessPolicies() (map[string]portainer.K8sNamespaceAccessPolicy, error) { func (kcl *KubeClient) GetNamespaceAccessPolicies() (map[string]portainer.K8sNamespaceAccessPolicy, error) {
+68
View File
@@ -0,0 +1,68 @@
package cli
import (
"sync"
"testing"
portainer "github.com/portainer/portainer/api"
"github.com/stretchr/testify/assert"
ktypes "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
kfake "k8s.io/client-go/kubernetes/fake"
)
func Test_NamespaceAccessPoliciesDeleteNamespace_updatesPortainerConfig_whenConfigExists(t *testing.T) {
testcases := []struct {
name string
namespaceToDelete string
expectedConfig map[string]portainer.K8sNamespaceAccessPolicy
}{
{
name: "doesn't change config, when designated namespace absent",
namespaceToDelete: "missing-namespace",
expectedConfig: map[string]portainer.K8sNamespaceAccessPolicy{
"ns1": {UserAccessPolicies: portainer.UserAccessPolicies{2: {RoleID: 0}}},
"ns2": {UserAccessPolicies: portainer.UserAccessPolicies{2: {RoleID: 0}}},
},
},
{
name: "removes designated namespace from config, when namespace is present",
namespaceToDelete: "ns2",
expectedConfig: map[string]portainer.K8sNamespaceAccessPolicy{
"ns1": {UserAccessPolicies: portainer.UserAccessPolicies{2: {RoleID: 0}}},
},
},
}
for _, test := range testcases {
t.Run(test.name, func(t *testing.T) {
k := &KubeClient{
cli: kfake.NewSimpleClientset(),
instanceID: "instance",
lock: &sync.Mutex{},
}
config := &ktypes.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: portainerConfigMapName,
Namespace: portainerNamespace,
},
Data: map[string]string{
"NamespaceAccessPolicies": `{"ns1":{"UserAccessPolicies":{"2":{"RoleId":0}}}, "ns2":{"UserAccessPolicies":{"2":{"RoleId":0}}}}`,
},
}
_, err := k.cli.CoreV1().ConfigMaps(portainerNamespace).Create(config)
assert.NoError(t, err, "failed to create a portainer config")
defer func() {
k.cli.CoreV1().ConfigMaps(portainerNamespace).Delete(portainerConfigMapName, nil)
}()
err = k.NamespaceAccessPoliciesDeleteNamespace(test.namespaceToDelete)
assert.NoError(t, err, "failed to delete namespace")
policies, err := k.GetNamespaceAccessPolicies()
assert.NoError(t, err, "failed to fetch policies")
assert.Equal(t, test.expectedConfig, policies)
})
}
}
+4 -1
View File
@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"strconv" "strconv"
"sync"
cmap "github.com/orcaman/concurrent-map" cmap "github.com/orcaman/concurrent-map"
@@ -25,8 +26,9 @@ type (
// KubeClient represent a service used to execute Kubernetes operations // KubeClient represent a service used to execute Kubernetes operations
KubeClient struct { KubeClient struct {
cli *kubernetes.Clientset cli kubernetes.Interface
instanceID string instanceID string
lock *sync.Mutex
} }
) )
@@ -72,6 +74,7 @@ func (factory *ClientFactory) createKubeClient(endpoint *portainer.Endpoint) (po
kubecli := &KubeClient{ kubecli := &KubeClient{
cli: cli, cli: cli,
instanceID: factory.instanceID, instanceID: factory.instanceID,
lock: &sync.Mutex{},
} }
return kubecli, nil return kubecli, nil
+7 -2
View File
@@ -14,13 +14,18 @@ import (
// StartExecProcess will start an exec process inside a container located inside a pod inside a specific namespace // StartExecProcess will start an exec process inside a container located inside a pod inside a specific namespace
// using the specified command. The stdin parameter will be bound to the stdin process and the stdout process will write // using the specified command. The stdin parameter will be bound to the stdin process and the stdout process will write
// to the stdout parameter. // to the stdout parameter.
// This function only works against a local endpoint using an in-cluster config. // This function only works against a local endpoint using an in-cluster config with the user's SA token.
func (kcl *KubeClient) StartExecProcess(namespace, podName, containerName string, command []string, stdin io.Reader, stdout io.Writer) error { func (kcl *KubeClient) StartExecProcess(token string, useAdminToken bool, namespace, podName, containerName string, command []string, stdin io.Reader, stdout io.Writer) error {
config, err := rest.InClusterConfig() config, err := rest.InClusterConfig()
if err != nil { if err != nil {
return err return err
} }
if !useAdminToken {
config.BearerToken = token
config.BearerTokenFile = ""
}
req := kcl.cli.CoreV1().RESTClient(). req := kcl.cli.CoreV1().RESTClient().
Post(). Post().
Resource("pods"). Resource("pods").
+4 -5
View File
@@ -9,7 +9,6 @@ import (
"mime" "mime"
"net/http" "net/http"
"net/url" "net/url"
"time"
"golang.org/x/oauth2" "golang.org/x/oauth2"
@@ -27,18 +26,18 @@ func NewService() *Service {
// Authenticate takes an access code and exchanges it for an access token from portainer OAuthSettings token endpoint. // Authenticate takes an access code and exchanges it for an access token from portainer OAuthSettings token endpoint.
// On success, it will then return the username and token expiry time associated to authenticated user by fetching this information // On success, it will then return the username and token expiry time associated to authenticated user by fetching this information
// from the resource server and matching it with the user identifier setting. // from the resource server and matching it with the user identifier setting.
func (*Service) Authenticate(code string, configuration *portainer.OAuthSettings) (string, *time.Time, error) { func (*Service) Authenticate(code string, configuration *portainer.OAuthSettings) (string, error) {
token, err := getOAuthToken(code, configuration) token, err := getOAuthToken(code, configuration)
if err != nil { if err != nil {
log.Printf("[DEBUG] - Failed retrieving access token: %v", err) log.Printf("[DEBUG] - Failed retrieving access token: %v", err)
return "", nil, err return "", err
} }
username, err := getUsername(token.AccessToken, configuration) username, err := getUsername(token.AccessToken, configuration)
if err != nil { if err != nil {
log.Printf("[DEBUG] - Failed retrieving oauth user name: %v", err) log.Printf("[DEBUG] - Failed retrieving oauth user name: %v", err)
return "", nil, err return "", err
} }
return username, &token.Expiry, nil return username, nil
} }
func getOAuthToken(code string, configuration *portainer.OAuthSettings) (*oauth2.Token, error) { func getOAuthToken(code string, configuration *portainer.OAuthSettings) (*oauth2.Token, error) {
+6 -4
View File
@@ -2,6 +2,7 @@ package portainer
import ( import (
"io" "io"
"net/http"
"time" "time"
gittypes "github.com/portainer/portainer/api/git/types" gittypes "github.com/portainer/portainer/api/git/types"
@@ -1164,14 +1165,15 @@ type (
KubeClient interface { KubeClient interface {
SetupUserServiceAccount(userID int, teamIDs []int) error SetupUserServiceAccount(userID int, teamIDs []int) error
GetServiceAccountBearerToken(userID int) (string, error) GetServiceAccountBearerToken(userID int) (string, error)
StartExecProcess(namespace, podName, containerName string, command []string, stdin io.Reader, stdout io.Writer) error StartExecProcess(token string, useAdminToken bool, namespace, podName, containerName string, command []string, stdin io.Reader, stdout io.Writer) error
NamespaceAccessPoliciesDeleteNamespace(namespace string) error
GetNamespaceAccessPolicies() (map[string]K8sNamespaceAccessPolicy, error) GetNamespaceAccessPolicies() (map[string]K8sNamespaceAccessPolicy, error)
UpdateNamespaceAccessPolicies(accessPolicies map[string]K8sNamespaceAccessPolicy) error UpdateNamespaceAccessPolicies(accessPolicies map[string]K8sNamespaceAccessPolicy) error
} }
// KubernetesDeployer represents a service to deploy a manifest inside a Kubernetes endpoint // KubernetesDeployer represents a service to deploy a manifest inside a Kubernetes endpoint
KubernetesDeployer interface { KubernetesDeployer interface {
Deploy(endpoint *Endpoint, data string, namespace string) (string, error) Deploy(request *http.Request, endpoint *Endpoint, data string, namespace string) (string, error)
ConvertCompose(data string) ([]byte, error) ConvertCompose(data string) ([]byte, error)
} }
@@ -1189,7 +1191,7 @@ type (
// OAuthService represents a service used to authenticate users using OAuth // OAuthService represents a service used to authenticate users using OAuth
OAuthService interface { OAuthService interface {
Authenticate(code string, configuration *OAuthSettings) (string, *time.Time, error) Authenticate(code string, configuration *OAuthSettings) (string, error)
} }
// RegistryService represents a service for managing registry data // RegistryService represents a service for managing registry data
@@ -1341,7 +1343,7 @@ type (
const ( const (
// APIVersion is the version number of the Portainer API // APIVersion is the version number of the Portainer API
APIVersion = "2.6.0" APIVersion = "2.6.2"
// DBVersion is the version number of the Portainer database // DBVersion is the version number of the Portainer database
DBVersion = 30 DBVersion = 30
// ComposeSyntaxMaxVersion is a maximum supported version of the docker compose syntax // ComposeSyntaxMaxVersion is a maximum supported version of the docker compose syntax
@@ -19,7 +19,7 @@ export default class porImageRegistryContainerController {
if (this.EndpointHelper.isAgentEndpoint(this.endpoint) || this.EndpointHelper.isLocalEndpoint(this.endpoint)) { if (this.EndpointHelper.isAgentEndpoint(this.endpoint) || this.EndpointHelper.isLocalEndpoint(this.endpoint)) {
try { try {
this.pullRateLimits = await this.DockerHubService.checkRateLimits(this.endpoint); this.pullRateLimits = await this.DockerHubService.checkRateLimits(this.endpoint);
this.setValidity(this.pullRateLimits.remaining >= 0); this.setValidity(!this.pullRateLimits.limit || (this.pullRateLimits.limit && this.pullRateLimits.remaining >= 0));
} catch (e) { } catch (e) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error('Failed loading DockerHub pull rate limits', e); console.error('Failed loading DockerHub pull rate limits', e);
@@ -48,7 +48,7 @@ angular.module('portainer.docker').controller('LogViewerController', [
}; };
this.downloadLogs = function () { this.downloadLogs = function () {
const data = new Blob([_.reduce(this.state.filteredLogs, (acc, log) => acc + '\n' + log, '')]); const data = new Blob([_.reduce(this.state.filteredLogs, (acc, log) => acc + '\n' + log.line, '')]);
FileSaver.saveAs(data, this.resourceName + '_logs.txt'); FileSaver.saveAs(data, this.resourceName + '_logs.txt');
}; };
}, },
@@ -3,7 +3,8 @@
<div style="margin: 15px;"> <div style="margin: 15px;">
<span class="btn btn-primary btn-sm" ng-click="$ctrl.copyYAML()"><i class="fa fa-copy space-right" aria-hidden="true"></i>Copy to clipboard</span> <span class="btn btn-primary btn-sm" ng-click="$ctrl.copyYAML()"><i class="fa fa-copy space-right" aria-hidden="true"></i>Copy to clipboard</span>
<span class="btn btn-primary btn-sm space-left" ng-click="$ctrl.toggleYAMLInspectorExpansion()"> <span class="btn btn-primary btn-sm space-left" ng-click="$ctrl.toggleYAMLInspectorExpansion()">
<i class="fa fa-{{ $ctrl.expanded ? 'minus' : 'plus' }} space-right" aria-hidden="true"></i>{{ $ctrl.expanded ? 'Collapse' : 'Expand' }}</span> <i class="fa fa-{{ $ctrl.expanded ? 'minus' : 'plus' }} space-right" aria-hidden="true"></i>{{ $ctrl.expanded ? 'Collapse' : 'Expand' }}</span
>
<span id="copyNotificationYAML" style="margin-left: 7px; display: none; color: #23ae89;" class="small"> <i class="fa fa-check" aria-hidden="true"></i> copied </span> <span id="copyNotificationYAML" style="margin-left: 7px; display: none; color: #23ae89;" class="small"> <i class="fa fa-check" aria-hidden="true"></i> copied </span>
</div> </div>
</div> </div>
+2 -2
View File
@@ -261,7 +261,7 @@ class KubernetesApplicationConverter {
return res; return res;
} }
static applicationToFormValues(app, resourcePools, configurations, persistentVolumeClaims, nodesLabels) { static applicationToFormValues(app, resourcePools, configurations, persistentVolumeClaims, nodesLabels, ingresses) {
const res = new KubernetesApplicationFormValues(); const res = new KubernetesApplicationFormValues();
res.ApplicationType = app.ApplicationType; res.ApplicationType = app.ApplicationType;
res.ResourcePool = _.find(resourcePools, ['Namespace.Name', app.ResourcePool]); res.ResourcePool = _.find(resourcePools, ['Namespace.Name', app.ResourcePool]);
@@ -278,7 +278,7 @@ class KubernetesApplicationConverter {
res.PersistedFolders = KubernetesApplicationHelper.generatePersistedFoldersFormValuesFromPersistedFolders(app.PersistedFolders, persistentVolumeClaims); // generate from PVC and app.PersistedFolders res.PersistedFolders = KubernetesApplicationHelper.generatePersistedFoldersFormValuesFromPersistedFolders(app.PersistedFolders, persistentVolumeClaims); // generate from PVC and app.PersistedFolders
res.Configurations = KubernetesApplicationHelper.generateConfigurationFormValuesFromEnvAndVolumes(app.Env, app.ConfigurationVolumes, configurations); res.Configurations = KubernetesApplicationHelper.generateConfigurationFormValuesFromEnvAndVolumes(app.Env, app.ConfigurationVolumes, configurations);
res.AutoScaler = KubernetesApplicationHelper.generateAutoScalerFormValueFromHorizontalPodAutoScaler(app.AutoScaler, res.ReplicaCount); res.AutoScaler = KubernetesApplicationHelper.generateAutoScalerFormValueFromHorizontalPodAutoScaler(app.AutoScaler, res.ReplicaCount);
res.PublishedPorts = KubernetesApplicationHelper.generatePublishedPortsFormValuesFromPublishedPorts(app.ServiceType, app.PublishedPorts); res.PublishedPorts = KubernetesApplicationHelper.generatePublishedPortsFormValuesFromPublishedPorts(app.ServiceType, app.PublishedPorts, ingresses);
res.Containers = app.Containers; res.Containers = app.Containers;
const isIngress = _.filter(res.PublishedPorts, (p) => p.IngressName).length; const isIngress = _.filter(res.PublishedPorts, (p) => p.IngressName).length;
+2 -1
View File
@@ -274,7 +274,7 @@ class KubernetesApplicationHelper {
/* #endregion */ /* #endregion */
/* #region PUBLISHED PORTS FV <> PUBLISHED PORTS */ /* #region PUBLISHED PORTS FV <> PUBLISHED PORTS */
static generatePublishedPortsFormValuesFromPublishedPorts(serviceType, publishedPorts) { static generatePublishedPortsFormValuesFromPublishedPorts(serviceType, publishedPorts, ingress) {
const generatePort = (port, rule) => { const generatePort = (port, rule) => {
const res = new KubernetesApplicationPublishedPortFormValue(); const res = new KubernetesApplicationPublishedPortFormValue();
res.IsNew = false; res.IsNew = false;
@@ -282,6 +282,7 @@ class KubernetesApplicationHelper {
res.IngressName = rule.IngressName; res.IngressName = rule.IngressName;
res.IngressRoute = rule.Path; res.IngressRoute = rule.Path;
res.IngressHost = rule.Host; res.IngressHost = rule.Host;
res.IngressHosts = ingress && ingress.find((i) => i.Name === rule.IngressName).Hosts;
} }
res.Protocol = port.Protocol; res.Protocol = port.Protocol;
res.ContainerPort = port.TargetPort; res.ContainerPort = port.TargetPort;
+4 -2
View File
@@ -57,7 +57,9 @@ export class KubernetesIngressConverter {
rule.IngressName = ingress.Name; rule.IngressName = ingress.Name;
rule.ServiceName = serviceName; rule.ServiceName = serviceName;
rule.Port = p.ContainerPort; rule.Port = p.ContainerPort;
rule.Path = _.startsWith(p.IngressRoute, '/') ? p.IngressRoute : '/' + p.IngressRoute; if (p.IngressRoute) {
rule.Path = _.startsWith(p.IngressRoute, '/') ? p.IngressRoute : '/' + p.IngressRoute;
}
rule.Host = p.IngressHost; rule.Host = p.IngressHost;
ingress.Paths.push(rule); ingress.Paths.push(rule);
} }
@@ -171,7 +173,7 @@ export class KubernetesIngressConverter {
res.spec.rules = []; res.spec.rules = [];
_.forEach(data.Hosts, (host) => { _.forEach(data.Hosts, (host) => {
if (!host.NeedsDeletion) { if (!host.NeedsDeletion) {
res.spec.rules.push({ host: host.Host }); res.spec.rules.push({ host: host.Host || host });
} }
}); });
} else { } else {
@@ -124,6 +124,7 @@ export function KubernetesApplicationPublishedPortFormValue() {
IngressName: undefined, IngressName: undefined,
IngressRoute: undefined, IngressRoute: undefined,
IngressHost: undefined, IngressHost: undefined,
IngressHosts: [],
}; };
} }
@@ -1368,7 +1368,7 @@
class="form-control" class="form-control"
name="ingress_hostname_{{ $index }}" name="ingress_hostname_{{ $index }}"
ng-model="publishedPort.IngressHost" ng-model="publishedPort.IngressHost"
ng-options="host as (host | kubernetesApplicationIngressEmptyHostname) for host in ctrl.ingressHostnames" ng-options="host as (host | kubernetesApplicationIngressEmptyHostname) for host in publishedPort.IngressHosts"
ng-change="ctrl.onChangePublishedPorts()" ng-change="ctrl.onChangePublishedPorts()"
ng-disabled="ctrl.disableLoadBalancerEdit() || ctrl.isEditAndNotNewPublishedPort($index)" ng-disabled="ctrl.disableLoadBalancerEdit() || ctrl.isEditAndNotNewPublishedPort($index)"
> >
@@ -321,6 +321,7 @@ class KubernetesCreateApplicationController {
const ingresses = this.filteredIngresses; const ingresses = this.filteredIngresses;
p.IngressName = ingresses && ingresses.length ? ingresses[0].Name : undefined; p.IngressName = ingresses && ingresses.length ? ingresses[0].Name : undefined;
p.IngressHost = ingresses && ingresses.length ? ingresses[0].Hosts[0] : undefined; p.IngressHost = ingresses && ingresses.length ? ingresses[0].Hosts[0] : undefined;
p.IngressHosts = ingresses && ingresses.length ? ingresses[0].Hosts : undefined;
if (this.formValues.PublishedPorts.length) { if (this.formValues.PublishedPorts.length) {
p.Protocol = this.formValues.PublishedPorts[0].Protocol; p.Protocol = this.formValues.PublishedPorts[0].Protocol;
} }
@@ -388,6 +389,7 @@ class KubernetesCreateApplicationController {
onChangePortMappingIngress(index) { onChangePortMappingIngress(index) {
const publishedPort = this.formValues.PublishedPorts[index]; const publishedPort = this.formValues.PublishedPorts[index];
const ingress = _.find(this.filteredIngresses, { Name: publishedPort.IngressName }); const ingress = _.find(this.filteredIngresses, { Name: publishedPort.IngressName });
publishedPort.IngressHosts = ingress.Hosts;
this.ingressHostnames = ingress.Hosts; this.ingressHostnames = ingress.Hosts;
publishedPort.IngressHost = this.ingressHostnames.length ? this.ingressHostnames[0] : []; publishedPort.IngressHost = this.ingressHostnames.length ? this.ingressHostnames[0] : [];
this.onChangePublishedPorts(); this.onChangePublishedPorts();
@@ -972,7 +974,8 @@ class KubernetesCreateApplicationController {
this.resourcePools, this.resourcePools,
this.configurations, this.configurations,
this.persistentVolumeClaims, this.persistentVolumeClaims,
this.nodesLabels this.nodesLabels,
this.filteredIngresses
); );
this.formValues.OriginalIngresses = this.filteredIngresses; this.formValues.OriginalIngresses = this.filteredIngresses;
this.savedFormValues = angular.copy(this.formValues); this.savedFormValues = angular.copy(this.formValues);
@@ -172,6 +172,7 @@ class KubernetesCreateResourcePoolController {
this.endpoint = endpoint; this.endpoint = endpoint;
this.defaults = KubernetesResourceQuotaDefaults; this.defaults = KubernetesResourceQuotaDefaults;
this.formValues = new KubernetesResourcePoolFormValues(this.defaults); this.formValues = new KubernetesResourcePoolFormValues(this.defaults);
this.formValues.HasQuota = true;
this.state = { this.state = {
actionInProgress: false, actionInProgress: false,
@@ -1,12 +1,4 @@
import { KEY_REGEX, VALUE_REGEX } from '@/portainer/helpers/env-vars';
class EnvironmentVariablesSimpleModeItemController { class EnvironmentVariablesSimpleModeItemController {
/* @ngInject */
constructor() {
this.KEY_REGEX = KEY_REGEX;
this.VALUE_REGEX = VALUE_REGEX;
}
onChangeName(name) { onChangeName(name) {
const fieldIsInvalid = typeof name === 'undefined'; const fieldIsInvalid = typeof name === 'undefined';
if (fieldIsInvalid) { if (fieldIsInvalid) {
@@ -9,7 +9,6 @@
placeholder="e.g. FOO" placeholder="e.g. FOO"
ng-model="$ctrl.variable.name" ng-model="$ctrl.variable.name"
ng-disabled="$ctrl.variable.added" ng-disabled="$ctrl.variable.added"
ng-pattern="$ctrl.KEY_REGEX"
ng-change="$ctrl.onChangeName($ctrl.variable.name)" ng-change="$ctrl.onChangeName($ctrl.variable.name)"
required required
/> />
@@ -36,7 +35,6 @@
ng-model="$ctrl.variable.value" ng-model="$ctrl.variable.value"
placeholder="e.g. bar" placeholder="e.g. bar"
ng-trim="false" ng-trim="false"
ng-pattern="$ctrl.VALUE_REGEX"
name="value" name="value"
ng-change="$ctrl.onChangeValue($ctrl.variable.value)" ng-change="$ctrl.onChangeValue($ctrl.variable.value)"
/> />
+3 -4
View File
@@ -1,7 +1,6 @@
import _ from 'lodash-es'; import _ from 'lodash-es';
export const KEY_REGEX = /[a-zA-Z]([-_a-zA-Z0-9]*[a-zA-Z0-9])?/.source; export const KEY_REGEX = /(.+)/.source;
export const VALUE_REGEX = /(.*)?/.source; export const VALUE_REGEX = /(.*)?/.source;
const KEY_VALUE_REGEX = new RegExp(`^(${KEY_REGEX})\\s*=(${VALUE_REGEX})$`); const KEY_VALUE_REGEX = new RegExp(`^(${KEY_REGEX})\\s*=(${VALUE_REGEX})$`);
@@ -16,7 +15,7 @@ export function parseDotEnvFile(src) {
return parseArrayOfStrings( return parseArrayOfStrings(
_.compact(src.split(NEWLINES_REGEX)) _.compact(src.split(NEWLINES_REGEX))
.map((v) => v.trim()) .map((v) => v.trim())
.filter((v) => !v.startsWith('#')) .filter((v) => !v.startsWith('#') && v !== '')
); );
} }
@@ -40,7 +39,7 @@ export function parseArrayOfStrings(array) {
const parsedKeyValArr = variableString.trim().match(KEY_VALUE_REGEX); const parsedKeyValArr = variableString.trim().match(KEY_VALUE_REGEX);
if (parsedKeyValArr != null && parsedKeyValArr.length > 4) { if (parsedKeyValArr != null && parsedKeyValArr.length > 4) {
return { name: parsedKeyValArr[1], value: parsedKeyValArr[3] || '' }; return { name: parsedKeyValArr[1].trim(), value: parsedKeyValArr[3].trim() || '' };
} }
}) })
); );
+5
View File
@@ -26,6 +26,11 @@ angular.module('portainer.app').factory('ChartService', [
}, },
}, },
}, },
layout: {
padding: {
left: 15,
},
},
hover: { animationDuration: 0 }, hover: { animationDuration: 0 },
scales: { scales: {
yAxes: [ yAxes: [
+4 -4
View File
@@ -191,7 +191,7 @@ function shell_download_docker_binary(p, a) {
var ip = ps[p] === undefined ? p : ps[p]; var ip = ps[p] === undefined ? p : ps[p];
var ia = as[a] === undefined ? a : as[a]; var ia = as[a] === undefined ? a : as[a];
var binaryVersion = p === 'windows' ? '<%= binaries.dockerWindowsVersion %>' : '<%= binaries.dockerLinuxVersion %>'; var binaryVersion = p === 'windows' ? '<%= binaries.dockerWindowsVersion %>' : '<%= binaries.dockerLinuxVersion %>';
return [ return [
'if [ -f dist/docker ] || [ -f dist/docker.exe ]; then', 'if [ -f dist/docker ] || [ -f dist/docker.exe ]; then',
'echo "docker binary exists";', 'echo "docker binary exists";',
@@ -207,7 +207,7 @@ function shell_download_docker_compose_binary(p, a) {
var ip = ps[p] || p; var ip = ps[p] || p;
var ia = as[a] || a; var ia = as[a] || a;
var binaryVersion = p === 'windows' ? '<%= binaries.dockerWindowsComposeVersion %>' : '<%= binaries.dockerLinuxComposeVersion %>'; var binaryVersion = p === 'windows' ? '<%= binaries.dockerWindowsComposeVersion %>' : '<%= binaries.dockerLinuxComposeVersion %>';
return [ return [
'if [ -f dist/docker-compose ] || [ -f dist/docker-compose.exe ]; then', 'if [ -f dist/docker-compose ] || [ -f dist/docker-compose.exe ]; then',
'echo "Docker Compose binary exists";', 'echo "Docker Compose binary exists";',
@@ -219,7 +219,7 @@ function shell_download_docker_compose_binary(p, a) {
function shell_download_kompose_binary(p, a) { function shell_download_kompose_binary(p, a) {
var binaryVersion = '<%= binaries.komposeVersion %>'; var binaryVersion = '<%= binaries.komposeVersion %>';
return [ return [
'if [ -f dist/kompose ] || [ -f dist/kompose.exe ]; then', 'if [ -f dist/kompose ] || [ -f dist/kompose.exe ]; then',
'echo "kompose binary exists";', 'echo "kompose binary exists";',
@@ -231,7 +231,7 @@ function shell_download_kompose_binary(p, a) {
function shell_download_kubectl_binary(p, a) { function shell_download_kubectl_binary(p, a) {
var binaryVersion = '<%= binaries.kubectlVersion %>'; var binaryVersion = '<%= binaries.kubectlVersion %>';
return [ return [
'if [ -f dist/kubectl ] || [ -f dist/kubectl.exe ]; then', 'if [ -f dist/kubectl ] || [ -f dist/kubectl.exe ]; then',
'echo "kubectl binary exists";', 'echo "kubectl binary exists";',
+1 -1
View File
@@ -2,7 +2,7 @@
"author": "Portainer.io", "author": "Portainer.io",
"name": "portainer", "name": "portainer",
"homepage": "http://portainer.io", "homepage": "http://portainer.io",
"version": "2.6.0", "version": "2.6.2",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git@github.com:portainer/portainer.git" "url": "git@github.com:portainer/portainer.git"