1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-05 13:55:21 +02:00

feat(kubernetes): introduce kubernetes support (#3987)

* feat(kubernetes): fix duplicate published mode

* feat(kubernetes): group port mappings by applications

* feat(kubernetes): updated UX

* feat(kubernetes): updated UX

* feat(kubernetes): new applications list view

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

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

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

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

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

* refactor(kubernetes): refactor services

* create view node detail

* compute node status

* compute resource reservations

* resource reservation progress bar

* create applications node datatable

* fix(kubernetes): fix invalid method name

* feat(kubernetes): minor UI changes

* feat(kubernetes): update application inspect UI

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

* fix(kubernetes): minor fixes on applications view

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

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

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

* refacto(kubernetes): clean status and resource computation

* fix(kubernetes): remove a bad line

* feat(kubernetes): update application detail view

* feat(kubernetes): change few things on view

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

* refacto(kubernetes): remove old functions

* feat(kubernetes): add application pod logs

* fix(kubernetes): PR #13

* feat(kubernetes): Enable quotas by default

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

* feat(kubernetes): UI changes

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

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

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

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

* feat(kubernetes): update podService

* refacto(kubernetes): rename nodeInspect as node

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

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

* refacto(kubernetes): remove not needed lines

* refacto(kubernetes) extract usageLevelInfo as html filter

* refacto(kubernetes): no line break for params

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

* refacto(kubernetes): remove bad indentations

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

* refacto(kubernetes): ES6 format

* refacto(kubernetes): format

* refacto(kubernetes): format

* refacto(kubernetes): add refresh callback for nodeapplicationsdatatable

* refacto(kubernetes): change if else structure

* refactor(kubernetes): files naming and format

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

* feat(kubernetes): minor UI update

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

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

* fix(kubernetes): format

* feat(kubernetes): minor UI update

* feat(kubernetes): add support for container console

* fix(kubernetes): fix a merge issue

* feat(kubernetes): update container console UI

* fix(api): fix typo

* feat(api): proxy pod websocket to agent

* fix(api): fix websocket pod proxy

* refactor(kubernetes): uniformize k8s merge comments

* refactor(kubernetes): update consoleController

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

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

* add new datatable

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

* refacto(kubernetes): factorise computeResourceReservation

* fix(kubernetes): colspan 6 to colspan 5

* fix(kubernetes): rename resourceReservationHelper into kubernetesResourceReservationHelper

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

* feat(kubernetes): add link to application name

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

* fix(kubernetes): change wording

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

* feat(kubernetes): persistent volume mockups

* feat(kubernetes): persistent volume mockups

* feat(kubernetes): update persisted folders mockups

* feat(kubernetes): endpoint configure storage access policies

* fix(kubernetes): restrict advanced deployment to admin

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

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

* feat(kubernetes): statefulset management for applications

* fix(kubernets): associate application and pods

* feat(kubernetes): statefulset support for applications

* refactor(kubernetes): rebase on pportainer/k8s

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

* fix(kubernetes): internal services showed as loadbalancer

* fix(kubernetes): service ports creation / parsing

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

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

* fix(kubernetes): statefulset replicas count display

* refactor(kubernetes): rebase on pportainer/k8s

* refactor(kubernetes): cleanup

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

* fix(kubernetes): remove mockup routes

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

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

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

* refactor(kubernetes): remove review comment

* chore(version): bump version number

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

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

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

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

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

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

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

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

* refactor(kubernetes): file rename and comment update

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

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

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

* fix(kubernetes): filter applications by node

* fix(kubernetes): remove js error

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

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

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

* feat(kubernetes): update instance count input behavior

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

* feat(kubernetes): resource pools labels

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

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

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

* feat(kubernetes): application labels

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

* style(kubernetes): revert StackName column labels

* fix(kubernetes): default displayed StackName

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

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

* refactor(kubernetes): routes as components

* refactor(kubernetes): use component  lifecycle hook

* refactor(kubernetes): files naming consistency

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

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

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

* refactor(kubernetes): remove unused helper

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

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

* feat(kubernetes): add cluster resource reservation

* fix(kubernetes): filter resource reservation with applications

* fix(kubernetes): fix indent

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

* fix(kubernetes): remove unused import

* refacto(kubernetes): add resourcereservation model

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

* refacto(kubernetes): getpods in applicationService getAll

* fix(kubernetes): let to const

* fix(kubernetes): remove unused podservice

* fix(kubernetes): fix computeResourceReservation

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

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

* feat(kubernetes): add configuration list view

* feat(kubernetes): add configurations datatable

* feat(kubernetes): add item selection

* feat(kubernetes): allow to remove configuration

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

* fix(kubernetes): configurations view as component

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

* fix(kubernetes): update import

* fix(kubernetes): remove secret delete payload

* fix(kubernetes): rename configuration model

* fix(kubernetes): remove configmap delete payload

* fix(Kubernetes): fix configuration getAsync

* fix(kubernetes): extract params as variables

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

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

* fix(kubernetes): fix kubernetes configurations datatable callback

* refacto(Kubernetes): extract find before if

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

* fix(Kubernetes): fix getASync

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

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

* feat(kubernetes): minor UI update

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

* feat(kubernetes): minor UI update

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

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

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

* feat(kubernetes): create configuration view

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

* fix(kubernetes): fix validation issues

* fix(kubernetes): fix wording

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

* fix(kubernetes): rename KubernetesConfigurationEntry to KubernetesConfigurationFormValuesDataEntry

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

* fix(kubernetes): fix some bugs

* refacto(kubernetes): renaming

* fix(kubernetes): fix few bugs

* fix(kubernetes): fix few bugs

* review(kubernetes): refactor notices

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

* feat(kubernetes): rename codeclimate file

* feat(kubernetes): re-enable codeclimate

* feat(project): update codeclimate configuration

* feat(project): update codeclimate configuration

* feat(project): update codeclimate configuration

* feat(kubernetes): minor UI update

* feat(project): update codeclimate

* feat(project): update codeclimate configuration

* feat(project): update codeclimate configuration

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

* feat(kubernetes): configuration details view

* fix(kubernetes): fix wording

* fix(kubernetes): fix update button

* fix(kubernetes): line indent

* refacto(kubernetes): remove conversion

* refacto(kubernetes): remove useless line

* refacto(kubernetes): remove useless lines

* fix(kubernetes): revert error handling

* fix(kubernetes): fix wording

* fix(kubernetes): revert line deletion

* refacto(kubernetes): change data mapping

* fix(kubernetes): create before delete

* fix(kubernetes): fix duplicate bug

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

* feat(kubernetes): application configuration mockups

* feat(kubernetes): update mockup

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

* feat(kubernetes): app create - configuration support

* refactor(kubernetes): more generic configuration conversion function

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

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

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

* feat(kubernetes): update Cluster icon in sidebar

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

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

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

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

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

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

* limits instead of requests (#121)

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

* feat(kubernetes): volume details

* fix(kubernetes): yaml not showed

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

* feat(kubernetes): expandable stacks list

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

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

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

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

* feat(kubernetes): automatically select a configuration

* refactor(kubernetes): remove comment

* feat(kubernetes): minor UI update

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

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

* feat(kubernetes): prevent removal of system configurations

* fix(kubernetes): KubernetesNamespaceHelper is not a function

* refacto(kubernetes): change prevent removal pattern

* fix(kubernetes): remove unused dependencies

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

* fix(kubernetes): fix used configurations

* fix(kubernetes): remove console log

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

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

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

* fix(kubernetes): remove feat on bad branch

* fix(kubernetes): rename configuration types

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

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

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

* fix(kubernetes): remove feat on bad branch

* fix(kubernetes): rename configuration types

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

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

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

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

* feat(kubernetes): review HTML tags indentation

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

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

* fix(kubernetes): filter pods by node

* fix(kubernetes): fix applications by node filter

* fix(kubernetes): filter pods by node

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

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

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

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

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

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

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

* fix(kubernetes): fix applications by node filter

* fix(kubernetes): revert commit on bad branch

* refacto(kubernetes): refacto configmap get yaml function

* refacto(kubernetes): add yaml into converter

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

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

* feat(kubernetes): improve application details view

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

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

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

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

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

* refactor(css): remove comment

* feat(kubernetes): updated loading pattern

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

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

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

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

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

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

* feat(kubernetes): rebase on k8s branch

* feat(kubernetes): update icon size

* refactor(kubernetes): update indentation and tag format

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

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

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

* feat(kubernetes): update configuration UI/UX

* feat(kubernetes): update configuration UX

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

* fix(kubernetes): Invalid value for a configuration

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

* refactor(kubernetes): remove unneeded line

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

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

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

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

* feat(kubernetes): hide system configurations

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

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

* fix(kubernetes): fix few issue in volumesDatatableController

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

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

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

* feat(kubernetes): event warning indicator for applications

* refactor(kubernetes): refactor events indicator logic

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

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

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

* feat(kubernetes): revised application details view

* refactor(kubernetes): remove comment

* feat(kubernetes): rebase on k8s

* refactor(kubernetes): remove extra line

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

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

* feat(kubernetes): stack logs

* fix(kubernetes): ignore starting pods

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

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

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

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

* fix(kubernetes): Invalid CPU unit for node

* fix(kubernetes): Invalid CPU unit for node

* refacto(kubernetes): extract parseCPU function in helper

* refacto(kubernetes): rewrite parseCPU function

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

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

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

* fix(kubernetes): remove system resources filter

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

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

* feat(kubernetes): update access user message

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

* feat(kubernetes): review responsiveness of port mappings

* feat(kubernetes): clarify table settings

* feat(kubernetes): add resource reservation summary message

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

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

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

* feat(kubernetes): application stack update

* feat(kubernetes): change services stacks

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

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

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

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

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

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

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

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

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

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

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

* refactor(kubernetes): remove comment

* feat(kubernetes): minor UI update

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

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

* chore(version): bump version number

* feat(kubernetes): disable update notice

* feat(kubernetes): minor UI update

* feat(kubernetes): minor UI update

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

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

* feat(kubernetes): add a dns compliant validation

* fix(kubernetes): fix port range validation

* feat(kubernetes): lot of form validation

* feat(kubernetes): add lot of form validation

* feat(kubernetes): persisted folders size validation

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

* fix(kubernetes): fix createResourcePool button

* fix(kubernetes): change few things

* fix(kubernetes): fix slider memory

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

* fix(kubernetes): remove bad validation on keys

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

* feat(kubernetes): minor UI update

* fix(kubernetes):  revert on slider fix

* review(kubernetes): add future changes to do

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

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

* feat(kubernetes): remove Docker related content

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

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

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

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

* feat(kubernetes): configurations and volumes owner

* feat(kubernetes): rename datatables columns

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

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

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

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

* feat(kubernetes): tooltip on data access info

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

* feat(kubernetes): minor UI update

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

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

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

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

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

* feat(kubernetes): tag applications as external

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

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

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

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

* feat(kubernetes): add note to application details

* fix(kubernetes): remove eslintcache

* feat(kubernetes): update application note UI

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

* feat(kubernetes): fix with UI changes

* fix(kubernetes): change few things

* fix(kubernetes): remove duplicate button

* fix(kubernetes): just use a ternary

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

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

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

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

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

* feat(kubernetes): application to formValues conversion

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

* feat(kubernetes): draft app patch

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

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

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

* feat(kubernetes): kubernetes service patch mechanism

* feat(kubernetes): application edit

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

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

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

* feat(kubernetes): cancel button on edit view

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

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

* feat(kubernetes): minor UI update

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

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

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

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

* style(kubernetes): codeclimate

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

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

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

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

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

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

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

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

* feat(kubernetes): minor UI update

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

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

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

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

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

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

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

* style(kubernetes): code indent

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

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

* feat(kubernetes): minor UI update

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* feat(kubernetes): add ability to redeploy application

* feat(kubernetes): allow redeploy for external apps

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

This reverts commit 093375a7e93c1a07b845ebca1618da034a97fbcd.

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

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

* feat(kubernetes): prevent configuration properties edition

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

* feat(kubernetes): remove unused functions

* feat(kubernetes): minor UI update

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

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

* refactor(kubernetes): simplify FileReader usage

* refactor(kubernetes): Simplify FileReader usage

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

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

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

* refactor(kubernetes): refactor isFormValid functions in configurations

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

* refactor(kubernetes): remove duplicate lines

* refactor(kubernetes): remove commented line

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

* feat(kubernetes): Tag external volumes and configs

* feat(kubernetes): remove .eslintcache

* feat(kubernetes): change few things

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

* feat(kubernetes): minor UI update

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

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

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

* fix(kubernetes): persistent folder unit parsing

* fix(kubernetes): edit daemonset on RWO storage

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

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

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

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

* feat(kubernetes): fix validations issues

* feat(kubernetes): fix form validation

* feat(kubernetes): fix few things

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

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

* refactor(kubernetes): fix form validation issues

* refactor(kubernetes): fix missing value

* refactor(kubernetes): remove useless await

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

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

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

* feat(kubernetes): remove ROX references and checks

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

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

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

* feat(kubernetes): fix ui issue

* feat(kubernetes): change few things

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

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

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

* feat(kubernetes): application history for all types

* feat(kubernetes): deployment rollback

* feat(kubernetes): daemonset / statefulset rollback

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

* feat(kubernetes): minor UI changes

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

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

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

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

* feat(kubernetes): removes unused limits

* feat(kubernetes): Not so useless

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

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

* Update app/kubernetes/services/statefulSetService.js

* style(kubernetes): remove comment block

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

* chore(version): bump version number

* feat(kubernetes): update feedback panel text

* chore(app): add prettier to k8s

* style(app): apply prettier to k8s codebase

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

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

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

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

* chore(kubernetes): fix conflicts

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

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

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

* chore(app): update yarn.lock

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

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

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

* feat(kubernetes): application auto scaling details

* feat(kubernetes): minor UI update

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

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

Co-authored-by: xAt0mZ <baron_l@epitech.eu>
Co-authored-by: Maxime Bajeux <max.bajeux@gmail.com>
Co-authored-by: xAt0mZ <xAt0mZ@users.noreply.github.com>
This commit is contained in:
Anthony Lapenna 2020-07-06 11:21:03 +12:00 committed by GitHub
parent 24528ecea8
commit af6bea5acc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
361 changed files with 20794 additions and 2349 deletions

View file

@ -0,0 +1,343 @@
import _ from 'lodash-es';
import angular from 'angular';
import PortainerError from 'Portainer/error';
import { KubernetesApplicationTypes } from 'Kubernetes/models/application/models';
import KubernetesApplicationHelper from 'Kubernetes/helpers/application';
import KubernetesApplicationRollbackHelper from 'Kubernetes/helpers/application/rollback';
import KubernetesApplicationConverter from 'Kubernetes/converters/application';
import { KubernetesDeployment } from 'Kubernetes/models/deployment/models';
import { KubernetesStatefulSet } from 'Kubernetes/models/stateful-set/models';
import { KubernetesDaemonSet } from 'Kubernetes/models/daemon-set/models';
import { KubernetesApplication } from 'Kubernetes/models/application/models';
import KubernetesServiceHelper from 'Kubernetes/helpers/serviceHelper';
import { KubernetesHorizontalPodAutoScalerHelper } from 'Kubernetes/horizontal-pod-auto-scaler/helper';
class KubernetesApplicationService {
/* @ngInject */
constructor(
$async,
Authentication,
KubernetesDeploymentService,
KubernetesDaemonSetService,
KubernetesStatefulSetService,
KubernetesServiceService,
KubernetesSecretService,
KubernetesPersistentVolumeClaimService,
KubernetesNamespaceService,
KubernetesPodService,
KubernetesHistoryService,
KubernetesHorizontalPodAutoScalerService
) {
this.$async = $async;
this.Authentication = Authentication;
this.KubernetesDeploymentService = KubernetesDeploymentService;
this.KubernetesDaemonSetService = KubernetesDaemonSetService;
this.KubernetesStatefulSetService = KubernetesStatefulSetService;
this.KubernetesServiceService = KubernetesServiceService;
this.KubernetesSecretService = KubernetesSecretService;
this.KubernetesPersistentVolumeClaimService = KubernetesPersistentVolumeClaimService;
this.KubernetesNamespaceService = KubernetesNamespaceService;
this.KubernetesPodService = KubernetesPodService;
this.KubernetesHistoryService = KubernetesHistoryService;
this.KubernetesHorizontalPodAutoScalerService = KubernetesHorizontalPodAutoScalerService;
this.getAsync = this.getAsync.bind(this);
this.getAllAsync = this.getAllAsync.bind(this);
this.createAsync = this.createAsync.bind(this);
this.patchAsync = this.patchAsync.bind(this);
this.patchPartialAsync = this.patchPartialAsync.bind(this);
this.rollbackAsync = this.rollbackAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
/**
* UTILS
*/
_getApplicationApiService(app) {
let apiService;
if (app instanceof KubernetesDeployment || (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.DEPLOYMENT)) {
apiService = this.KubernetesDeploymentService;
} else if (app instanceof KubernetesDaemonSet || (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.DAEMONSET)) {
apiService = this.KubernetesDaemonSetService;
} else if (app instanceof KubernetesStatefulSet || (app instanceof KubernetesApplication && app.ApplicationType === KubernetesApplicationTypes.STATEFULSET)) {
apiService = this.KubernetesStatefulSetService;
} else {
throw new PortainerError('Unable to determine which association to use');
}
return apiService;
}
/**
* GET
*/
async getAsync(namespace, name) {
try {
const [deployment, daemonSet, statefulSet, pods, autoScalers] = await Promise.allSettled([
this.KubernetesDeploymentService.get(namespace, name),
this.KubernetesDaemonSetService.get(namespace, name),
this.KubernetesStatefulSetService.get(namespace, name),
this.KubernetesPodService.get(namespace),
this.KubernetesHorizontalPodAutoScalerService.get(namespace),
]);
let rootItem;
let converterFunction;
if (deployment.status === 'fulfilled') {
rootItem = deployment;
converterFunction = KubernetesApplicationConverter.apiDeploymentToApplication;
} else if (daemonSet.status === 'fulfilled') {
rootItem = daemonSet;
converterFunction = KubernetesApplicationConverter.apiDaemonSetToApplication;
} else if (statefulSet.status === 'fulfilled') {
rootItem = statefulSet;
converterFunction = KubernetesApplicationConverter.apiStatefulSetToapplication;
} else {
throw new PortainerError('Unable to determine which association to use');
}
const services = await this.KubernetesServiceService.get(namespace);
const boundService = KubernetesServiceHelper.findApplicationBoundService(services, rootItem.value.Raw);
const service = boundService ? await this.KubernetesServiceService.get(namespace, boundService.metadata.name) : {};
const application = converterFunction(rootItem.value.Raw, service.Raw);
application.Yaml = rootItem.value.Yaml;
application.Raw = rootItem.value.Raw;
application.Pods = KubernetesApplicationHelper.associatePodsAndApplication(pods.value, application.Raw);
const boundScaler = KubernetesHorizontalPodAutoScalerHelper.findApplicationBoundScaler(autoScalers.value, application);
const scaler = boundScaler ? await this.KubernetesHorizontalPodAutoScalerService.get(namespace, boundScaler.Name) : undefined;
application.AutoScaler = scaler;
await this.KubernetesHistoryService.get(application);
if (service.Yaml) {
application.Yaml += '---\n' + service.Yaml;
}
if (scaler && scaler.Yaml) {
application.Yaml += '---\n' + scaler.Yaml;
}
return application;
} catch (err) {
throw err;
}
}
async getAllAsync(namespace) {
try {
const namespaces = namespace ? [namespace] : _.map(await this.KubernetesNamespaceService.get(), 'Name');
const res = await Promise.all(
_.map(namespaces, async (ns) => {
const [deployments, daemonSets, statefulSets, services, pods] = await Promise.all([
this.KubernetesDeploymentService.get(ns),
this.KubernetesDaemonSetService.get(ns),
this.KubernetesStatefulSetService.get(ns),
this.KubernetesServiceService.get(ns),
this.KubernetesPodService.get(ns),
]);
const deploymentApplications = _.map(deployments, (item) => {
const service = KubernetesServiceHelper.findApplicationBoundService(services, item);
const application = KubernetesApplicationConverter.apiDeploymentToApplication(item, service);
application.Pods = KubernetesApplicationHelper.associatePodsAndApplication(pods, item);
return application;
});
const daemonSetApplications = _.map(daemonSets, (item) => {
const service = KubernetesServiceHelper.findApplicationBoundService(services, item);
const application = KubernetesApplicationConverter.apiDaemonSetToApplication(item, service);
application.Pods = KubernetesApplicationHelper.associatePodsAndApplication(pods, item);
return application;
});
const statefulSetApplications = _.map(statefulSets, (item) => {
const service = KubernetesServiceHelper.findApplicationBoundService(services, item);
const application = KubernetesApplicationConverter.apiStatefulSetToapplication(item, service);
application.Pods = KubernetesApplicationHelper.associatePodsAndApplication(pods, item);
return application;
});
return _.concat(deploymentApplications, daemonSetApplications, statefulSetApplications);
})
);
return _.flatten(res);
} catch (err) {
throw err;
}
}
get(namespace, name) {
if (name) {
return this.$async(this.getAsync, namespace, name);
}
return this.$async(this.getAllAsync, namespace);
}
/**
* CREATE
*/
// TODO: review
// resource creation flow
// should we keep formValues > Resource_1 || Resource_2
// or should we switch to formValues > Composite > Resource_1 || Resource_2
async createAsync(formValues) {
try {
let [app, headlessService, service, claims] = KubernetesApplicationConverter.applicationFormValuesToApplication(formValues);
if (service) {
await this.KubernetesServiceService.create(service);
}
const apiService = this._getApplicationApiService(app);
if (app instanceof KubernetesStatefulSet) {
app.VolumeClaims = claims;
headlessService = await this.KubernetesServiceService.create(headlessService);
app.ServiceName = headlessService.metadata.name;
} else {
const claimPromises = _.map(claims, (item) => {
if (!item.PreviousName) {
return this.KubernetesPersistentVolumeClaimService.create(item);
}
});
await Promise.all(_.without(claimPromises, undefined));
}
await apiService.create(app);
} catch (err) {
throw err;
}
}
create(formValues) {
return this.$async(this.createAsync, formValues);
}
/**
* PATCH
*/
// this function accepts KubernetesApplicationFormValues as parameters
async patchAsync(oldFormValues, newFormValues) {
try {
const [oldApp, oldHeadlessService, oldService, oldClaims] = KubernetesApplicationConverter.applicationFormValuesToApplication(oldFormValues);
const [newApp, newHeadlessService, newService, newClaims] = KubernetesApplicationConverter.applicationFormValuesToApplication(newFormValues);
const oldApiService = this._getApplicationApiService(oldApp);
const newApiService = this._getApplicationApiService(newApp);
if (oldApiService !== newApiService) {
await this.delete(oldApp);
if (oldService) {
await this.KubernetesServiceService.delete(oldService);
}
return await this.create(newFormValues);
}
if (newApp instanceof KubernetesStatefulSet) {
await this.KubernetesServiceService.patch(oldHeadlessService, newHeadlessService);
} else {
const claimPromises = _.map(newClaims, (newClaim) => {
if (!newClaim.PreviousName) {
return this.KubernetesPersistentVolumeClaimService.create(newClaim);
}
const oldClaim = _.find(oldClaims, { Name: newClaim.PreviousName });
return this.KubernetesPersistentVolumeClaimService.patch(oldClaim, newClaim);
});
await Promise.all(claimPromises);
}
await newApiService.patch(oldApp, newApp);
if (oldService && newService) {
await this.KubernetesServiceService.patch(oldService, newService);
} else if (!oldService && newService) {
await this.KubernetesServiceService.create(newService);
} else if (oldService && !newService) {
await this.KubernetesServiceService.delete(oldService);
}
} catch (err) {
throw err;
}
}
// this function accepts KubernetesApplication as parameters
async patchPartialAsync(oldApp, newApp) {
try {
const oldAppPayload = {
Name: oldApp.Name,
Namespace: oldApp.ResourcePool,
StackName: oldApp.StackName,
Note: oldApp.Note,
};
const newAppPayload = {
Name: newApp.Name,
Namespace: newApp.ResourcePool,
StackName: newApp.StackName,
Note: newApp.Note,
};
const apiService = this._getApplicationApiService(oldApp);
await apiService.patch(oldAppPayload, newAppPayload);
} catch (err) {
throw err;
}
}
// accept either formValues or applications as parameters
// depending on partial value
// true = KubernetesApplication
// false = KubernetesApplicationFormValues
patch(oldValues, newValues, partial = false) {
if (partial) {
return this.$async(this.patchPartialAsync, oldValues, newValues);
}
return this.$async(this.patchAsync, oldValues, newValues);
}
/**
* DELETE
*/
async deleteAsync(application) {
try {
const payload = {
Namespace: application.ResourcePool || application.Namespace,
Name: application.Name,
};
const servicePayload = angular.copy(payload);
servicePayload.Name = application.Name;
const apiService = this._getApplicationApiService(application);
await apiService.delete(payload);
if (apiService === this.KubernetesStatefulSetService) {
const headlessServicePayload = angular.copy(payload);
headlessServicePayload.Name = application instanceof KubernetesStatefulSet ? application.ServiceName : application.HeadlessServiceName;
await this.KubernetesServiceService.delete(headlessServicePayload);
}
if (application.ServiceType) {
await this.KubernetesServiceService.delete(servicePayload);
}
} catch (err) {
throw err;
}
}
delete(application) {
return this.$async(this.deleteAsync, application);
}
/**
* ROLLBACK
*/
async rollbackAsync(application, targetRevision) {
try {
const payload = KubernetesApplicationRollbackHelper.getPatchPayload(application, targetRevision);
const apiService = this._getApplicationApiService(application);
await apiService.rollback(application.ResourcePool, application.Name, payload);
} catch (err) {
throw err;
}
}
rollback(application, targetRevision) {
return this.$async(this.rollbackAsync, application, targetRevision);
}
}
export default KubernetesApplicationService;
angular.module('portainer.kubernetes').service('KubernetesApplicationService', KubernetesApplicationService);

View file

@ -0,0 +1,115 @@
import angular from 'angular';
import _ from 'lodash-es';
import PortainerError from 'Portainer/error';
import KubernetesConfigMapConverter from 'Kubernetes/converters/configMap';
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
class KubernetesConfigMapService {
/* @ngInject */
constructor($async, KubernetesConfigMaps) {
this.$async = $async;
this.KubernetesConfigMaps = KubernetesConfigMaps;
this.getAsync = this.getAsync.bind(this);
this.getAllAsync = this.getAllAsync.bind(this);
this.createAsync = this.createAsync.bind(this);
this.updateAsync = this.updateAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
/**
* GET
*/
async getAsync(namespace, name) {
try {
const params = new KubernetesCommonParams();
params.id = name;
const [raw, yaml] = await Promise.all([this.KubernetesConfigMaps(namespace).get(params).$promise, this.KubernetesConfigMaps(namespace).getYaml(params).$promise]);
const configMap = KubernetesConfigMapConverter.apiToConfigMap(raw, yaml);
return configMap;
} catch (err) {
if (err.status === 404) {
return KubernetesConfigMapConverter.defaultConfigMap(namespace, name);
}
throw new PortainerError('Unable to retrieve config map', err);
}
}
async getAllAsync(namespace) {
try {
const data = await this.KubernetesConfigMaps(namespace).get().$promise;
return _.map(data.items, (item) => KubernetesConfigMapConverter.apiToConfigMap(item));
} catch (err) {
throw new PortainerError('Unable to retrieve config maps', err);
}
}
get(namespace, name) {
if (name) {
return this.$async(this.getAsync, namespace, name);
}
return this.$async(this.getAllAsync, namespace);
}
/**
* CREATE
*/
async createAsync(config) {
try {
const payload = KubernetesConfigMapConverter.createPayload(config);
const params = {};
const namespace = payload.metadata.namespace;
const data = await this.KubernetesConfigMaps(namespace).create(params, payload).$promise;
return KubernetesConfigMapConverter.apiToConfigMap(data);
} catch (err) {
throw new PortainerError('Unable to create config map', err);
}
}
create(config) {
return this.$async(this.createAsync, config);
}
/**
* UPDATE
*/
async updateAsync(config) {
try {
if (!config.Id) {
return await this.create(config);
}
const payload = KubernetesConfigMapConverter.updatePayload(config);
const params = new KubernetesCommonParams();
params.id = payload.metadata.name;
const namespace = payload.metadata.namespace;
const data = await this.KubernetesConfigMaps(namespace).update(params, payload).$promise;
return KubernetesConfigMapConverter.apiToConfigMap(data);
} catch (err) {
throw new PortainerError('Unable to update config map', err);
}
}
update(config) {
return this.$async(this.updateAsync, config);
}
/**
* DELETE
*/
async deleteAsync(config) {
try {
const params = new KubernetesCommonParams();
params.id = config.Name;
const namespace = config.Namespace;
await this.KubernetesConfigMaps(namespace).delete(params).$promise;
} catch (err) {
throw new PortainerError('Unable to delete config map', err);
}
}
delete(config) {
return this.$async(this.deleteAsync, config);
}
}
export default KubernetesConfigMapService;
angular.module('portainer.kubernetes').service('KubernetesConfigMapService', KubernetesConfigMapService);

View file

@ -0,0 +1,128 @@
import angular from 'angular';
import _ from 'lodash-es';
import KubernetesConfigurationConverter from 'Kubernetes/converters/configuration';
import KubernetesConfigMapConverter from 'Kubernetes/converters/configMap';
import KubernetesSecretConverter from 'Kubernetes/converters/secret';
import { KubernetesConfigurationTypes } from 'Kubernetes/models/configuration/models';
class KubernetesConfigurationService {
/* @ngInject */
constructor($async, Authentication, KubernetesNamespaceService, KubernetesConfigMapService, KubernetesSecretService) {
this.$async = $async;
this.Authentication = Authentication;
this.KubernetesNamespaceService = KubernetesNamespaceService;
this.KubernetesConfigMapService = KubernetesConfigMapService;
this.KubernetesSecretService = KubernetesSecretService;
this.getAsync = this.getAsync.bind(this);
this.getAllAsync = this.getAllAsync.bind(this);
this.createAsync = this.createAsync.bind(this);
this.updateAsync = this.updateAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
/**
* GET
*/
async getAsync(namespace, name) {
try {
const [configMap, secret] = await Promise.allSettled([this.KubernetesConfigMapService.get(namespace, name), this.KubernetesSecretService.get(namespace, name)]);
let configuration;
if (secret.status === 'fulfilled') {
configuration = KubernetesConfigurationConverter.secretToConfiguration(secret.value);
return configuration;
}
configuration = KubernetesConfigurationConverter.configMapToConfiguration(configMap.value);
return configuration;
} catch (err) {
throw err;
}
}
async getAllAsync(namespace) {
try {
const namespaces = namespace ? [namespace] : _.map(await this.KubernetesNamespaceService.get(), 'Name');
const res = await Promise.all(
_.map(namespaces, async (ns) => {
const [configMaps, secrets] = await Promise.all([this.KubernetesConfigMapService.get(ns), this.KubernetesSecretService.get(ns)]);
const secretsConfigurations = _.map(secrets, (secret) => KubernetesConfigurationConverter.secretToConfiguration(secret));
const configMapsConfigurations = _.map(configMaps, (configMap) => KubernetesConfigurationConverter.configMapToConfiguration(configMap));
return _.concat(configMapsConfigurations, secretsConfigurations);
})
);
return _.flatten(res);
} catch (err) {
throw err;
}
}
get(namespace, name) {
if (name) {
return this.$async(this.getAsync, namespace, name);
}
return this.$async(this.getAllAsync, namespace);
}
/**
* CREATE
*/
async createAsync(formValues) {
try {
if (formValues.Type === KubernetesConfigurationTypes.CONFIGMAP) {
const configMap = KubernetesConfigMapConverter.configurationFormValuesToConfigMap(formValues);
await this.KubernetesConfigMapService.create(configMap);
} else {
const secret = KubernetesSecretConverter.configurationFormValuesToSecret(formValues);
await this.KubernetesSecretService.create(secret);
}
} catch (err) {
throw err;
}
}
create(formValues) {
return this.$async(this.createAsync, formValues);
}
/**
* UPDATE
*/
async updateAsync(formValues) {
try {
if (formValues.Type === KubernetesConfigurationTypes.CONFIGMAP) {
const configMap = KubernetesConfigMapConverter.configurationFormValuesToConfigMap(formValues);
await this.KubernetesConfigMapService.update(configMap);
} else {
const secret = KubernetesSecretConverter.configurationFormValuesToSecret(formValues);
await this.KubernetesSecretService.update(secret);
}
} catch (err) {
throw err;
}
}
update(config) {
return this.$async(this.updateAsync, config);
}
/**
* DELETE
*/
async deleteAsync(config) {
try {
if (config.Type === KubernetesConfigurationTypes.CONFIGMAP) {
await this.KubernetesConfigMapService.delete(config);
} else {
await this.KubernetesSecretService.delete(config);
}
} catch (err) {
throw err;
}
}
delete(config) {
return this.$async(this.deleteAsync, config);
}
}
export default KubernetesConfigurationService;
angular.module('portainer.kubernetes').service('KubernetesConfigurationService', KubernetesConfigurationService);

View file

@ -0,0 +1,31 @@
import angular from 'angular';
import PortainerError from 'Portainer/error';
class KubernetesControllerRevisionService {
/* @ngInject */
constructor($async, KubernetesControllerRevisions) {
this.$async = $async;
this.KubernetesControllerRevisions = KubernetesControllerRevisions;
this.getAllAsync = this.getAllAsync.bind(this);
}
/**
* GET
*/
async getAllAsync(namespace) {
try {
const data = await this.KubernetesControllerRevisions(namespace).get().$promise;
return data.items;
} catch (err) {
throw new PortainerError('Unable to retrieve ControllerRevisions', err);
}
}
get(namespace) {
return this.$async(this.getAllAsync, namespace);
}
}
export default KubernetesControllerRevisionService;
angular.module('portainer.kubernetes').service('KubernetesControllerRevisionService', KubernetesControllerRevisionService);

View file

@ -0,0 +1,133 @@
import angular from 'angular';
import PortainerError from 'Portainer/error';
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
import KubernetesDaemonSetConverter from 'Kubernetes/converters/daemonSet';
class KubernetesDaemonSetService {
/* @ngInject */
constructor($async, KubernetesDaemonSets) {
this.$async = $async;
this.KubernetesDaemonSets = KubernetesDaemonSets;
this.getAsync = this.getAsync.bind(this);
this.getAllAsync = this.getAllAsync.bind(this);
this.createAsync = this.createAsync.bind(this);
this.patchAsync = this.patchAsync.bind(this);
this.rollbackAsync = this.rollbackAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
/**
* GET
*/
async getAsync(namespace, name) {
try {
const params = new KubernetesCommonParams();
params.id = name;
const [raw, yaml] = await Promise.all([this.KubernetesDaemonSets(namespace).get(params).$promise, this.KubernetesDaemonSets(namespace).getYaml(params).$promise]);
const res = {
Raw: raw,
Yaml: yaml.data,
};
return res;
} catch (err) {
throw new PortainerError('Unable to retrieve DaemonSet', err);
}
}
async getAllAsync(namespace) {
try {
const data = await this.KubernetesDaemonSets(namespace).get().$promise;
return data.items;
} catch (err) {
throw new PortainerError('Unable to retrieve DaemonSets', err);
}
}
get(namespace, name) {
if (name) {
return this.$async(this.getAsync, namespace, name);
}
return this.$async(this.getAllAsync, namespace);
}
/**
* CREATE
*/
async createAsync(daemonSet) {
try {
const params = {};
const payload = KubernetesDaemonSetConverter.createPayload(daemonSet);
const namespace = payload.metadata.namespace;
const data = await this.KubernetesDaemonSets(namespace).create(params, payload).$promise;
return data;
} catch (err) {
throw new PortainerError('Unable to create daemonset', err);
}
}
create(daemonSet) {
return this.$async(this.createAsync, daemonSet);
}
/**
* PATCH
*/
async patchAsync(oldDaemonSet, newDaemonSet) {
try {
const params = new KubernetesCommonParams();
params.id = newDaemonSet.Name;
const namespace = newDaemonSet.Namespace;
const payload = KubernetesDaemonSetConverter.patchPayload(oldDaemonSet, newDaemonSet);
if (!payload.length) {
return;
}
const data = await this.KubernetesDaemonSets(namespace).patch(params, payload).$promise;
return data;
} catch (err) {
throw new PortainerError('Unable to patch daemonSet', err);
}
}
patch(oldDaemonSet, newDaemonSet) {
return this.$async(this.patchAsync, oldDaemonSet, newDaemonSet);
}
/**
* DELETE
*/
async deleteAsync(daemonSet) {
try {
const params = new KubernetesCommonParams();
params.id = daemonSet.Name;
const namespace = daemonSet.Namespace;
await this.KubernetesDaemonSets(namespace).delete(params).$promise;
} catch (err) {
throw new PortainerError('Unable to remove daemonset', err);
}
}
delete(daemonSet) {
return this.$async(this.deleteAsync, daemonSet);
}
/**
* ROLLBACK
*/
async rollbackAsync(namespace, name, payload) {
try {
const params = new KubernetesCommonParams();
params.id = name;
await this.KubernetesDaemonSets(namespace).rollback(params, payload).$promise;
} catch (err) {
throw new PortainerError('Unable to rollback daemonset', err);
}
}
rollback(namespace, name, payload) {
return this.$async(this.rollbackAsync, namespace, name, payload);
}
}
export default KubernetesDaemonSetService;
angular.module('portainer.kubernetes').service('KubernetesDaemonSetService', KubernetesDaemonSetService);

View file

@ -0,0 +1,133 @@
import angular from 'angular';
import PortainerError from 'Portainer/error';
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
import KubernetesDeploymentConverter from 'Kubernetes/converters/deployment';
class KubernetesDeploymentService {
/* @ngInject */
constructor($async, KubernetesDeployments) {
this.$async = $async;
this.KubernetesDeployments = KubernetesDeployments;
this.getAsync = this.getAsync.bind(this);
this.getAllAsync = this.getAllAsync.bind(this);
this.createAsync = this.createAsync.bind(this);
this.patchAsync = this.patchAsync.bind(this);
this.rollbackAsync = this.rollbackAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
/**
* GET
*/
async getAsync(namespace, name) {
try {
const params = new KubernetesCommonParams();
params.id = name;
const [raw, yaml] = await Promise.all([this.KubernetesDeployments(namespace).get(params).$promise, this.KubernetesDeployments(namespace).getYaml(params).$promise]);
const res = {
Raw: raw,
Yaml: yaml.data,
};
return res;
} catch (err) {
throw new PortainerError('Unable to retrieve Deployment', err);
}
}
async getAllAsync(namespace) {
try {
const data = await this.KubernetesDeployments(namespace).get().$promise;
return data.items;
} catch (err) {
throw new PortainerError('Unable to retrieve Deployments', err);
}
}
get(namespace, name) {
if (name) {
return this.$async(this.getAsync, namespace, name);
}
return this.$async(this.getAllAsync, namespace);
}
/**
* CREATE
*/
async createAsync(deployment) {
try {
const params = {};
const payload = KubernetesDeploymentConverter.createPayload(deployment);
const namespace = payload.metadata.namespace;
const data = await this.KubernetesDeployments(namespace).create(params, payload).$promise;
return data;
} catch (err) {
throw new PortainerError('Unable to create deployment', err);
}
}
create(deployment) {
return this.$async(this.createAsync, deployment);
}
/**
* PATCH
*/
async patchAsync(oldDeployment, newDeployment) {
try {
const params = new KubernetesCommonParams();
params.id = newDeployment.Name;
const namespace = newDeployment.Namespace;
const payload = KubernetesDeploymentConverter.patchPayload(oldDeployment, newDeployment);
if (!payload.length) {
return;
}
const data = await this.KubernetesDeployments(namespace).patch(params, payload).$promise;
return data;
} catch (err) {
throw new PortainerError('Unable to patch deployment', err);
}
}
patch(oldDeployment, newDeployment) {
return this.$async(this.patchAsync, oldDeployment, newDeployment);
}
/**
* DELETE
*/
async deleteAsync(deployment) {
try {
const params = new KubernetesCommonParams();
params.id = deployment.Name;
const namespace = deployment.Namespace;
await this.KubernetesDeployments(namespace).delete(params).$promise;
} catch (err) {
throw new PortainerError('Unable to remove deployment', err);
}
}
delete(deployment) {
return this.$async(this.deleteAsync, deployment);
}
/**
* ROLLBACK
*/
async rollbackAsync(namespace, name, payload) {
try {
const params = new KubernetesCommonParams();
params.id = name;
await this.KubernetesDeployments(namespace).rollback(params, payload).$promise;
} catch (err) {
throw new PortainerError('Unable to rollback deployment', err);
}
}
rollback(namespace, name, payload) {
return this.$async(this.rollbackAsync, namespace, name, payload);
}
}
export default KubernetesDeploymentService;
angular.module('portainer.kubernetes').service('KubernetesDeploymentService', KubernetesDeploymentService);

View file

@ -0,0 +1,34 @@
import _ from 'lodash-es';
import angular from 'angular';
import PortainerError from 'Portainer/error';
import KubernetesEventConverter from 'Kubernetes/converters/event';
class KubernetesEventService {
/* @ngInject */
constructor($async, KubernetesEvents) {
this.$async = $async;
this.KubernetesEvents = KubernetesEvents;
this.getAllAsync = this.getAllAsync.bind(this);
}
/**
* GET
*/
async getAllAsync(namespace) {
try {
const data = await this.KubernetesEvents(namespace).get().$promise;
const res = _.map(data.items, (item) => KubernetesEventConverter.apiToEvent(item));
return res;
} catch (err) {
throw new PortainerError('Unable to retrieve events', err);
}
}
get(namespace) {
return this.$async(this.getAllAsync, namespace);
}
}
export default KubernetesEventService;
angular.module('portainer.kubernetes').service('KubernetesEventService', KubernetesEventService);

View file

@ -0,0 +1,30 @@
import angular from 'angular';
import PortainerError from 'Portainer/error';
class KubernetesHealthService {
/* @ngInject */
constructor($async, KubernetesHealth) {
this.$async = $async;
this.KubernetesHealth = KubernetesHealth;
this.pingAsync = this.pingAsync.bind(this);
}
/**
* PING
*/
async pingAsync() {
try {
return await this.KubernetesHealth.ping().$promise;
} catch (err) {
throw new PortainerError('Unable to retrieve environment health', err);
}
}
ping() {
return this.$async(this.pingAsync);
}
}
export default KubernetesHealthService;
angular.module('portainer.kubernetes').service('KubernetesHealthService', KubernetesHealthService);

View file

@ -0,0 +1,54 @@
import angular from 'angular';
import PortainerError from 'Portainer/error';
import KubernetesHistoryHelper from 'Kubernetes/helpers/history';
import { KubernetesApplicationTypes } from 'Kubernetes/models/application/models';
class KubernetesHistoryService {
/* @ngInject */
constructor($async, KubernetesReplicaSetService, KubernetesControllerRevisionService) {
this.$async = $async;
this.KubernetesReplicaSetService = KubernetesReplicaSetService;
this.KubernetesControllerRevisionService = KubernetesControllerRevisionService;
this.getAsync = this.getAsync.bind(this);
}
/**
* GET
*/
async getAsync(application) {
try {
const namespace = application.ResourcePool;
let rawRevisions;
switch (application.ApplicationType) {
case KubernetesApplicationTypes.DEPLOYMENT:
rawRevisions = await this.KubernetesReplicaSetService.get(namespace);
break;
case KubernetesApplicationTypes.DAEMONSET:
rawRevisions = await this.KubernetesControllerRevisionService.get(namespace);
break;
case KubernetesApplicationTypes.STATEFULSET:
rawRevisions = await this.KubernetesControllerRevisionService.get(namespace);
break;
default:
throw new PortainerError('Unable to determine which association to use');
}
const [currentRevision, revisionsList] = KubernetesHistoryHelper.getRevisions(rawRevisions, application);
application.CurrentRevision = currentRevision;
application.Revisions = revisionsList;
return application;
} catch (err) {
throw new PortainerError('', err);
}
}
get(application) {
return this.$async(this.getAsync, application);
}
}
export default KubernetesHistoryService;
angular.module('portainer.kubernetes').service('KubernetesHistoryService', KubernetesHistoryService);

View file

@ -0,0 +1,96 @@
import _ from 'lodash-es';
import angular from 'angular';
import PortainerError from 'Portainer/error';
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
import KubernetesNamespaceConverter from 'Kubernetes/converters/namespace';
import $allSettled from 'Portainer/services/allSettled';
class KubernetesNamespaceService {
/* @ngInject */
constructor($async, KubernetesNamespaces) {
this.$async = $async;
this.KubernetesNamespaces = KubernetesNamespaces;
this.getAsync = this.getAsync.bind(this);
this.getAllAsync = this.getAllAsync.bind(this);
this.createAsync = this.createAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
/**
* GET
*/
async getAsync(name) {
try {
const params = new KubernetesCommonParams();
params.id = name;
await this.KubernetesNamespaces().status(params).$promise;
const [raw, yaml] = await Promise.all([this.KubernetesNamespaces().get(params).$promise, this.KubernetesNamespaces().getYaml(params).$promise]);
return KubernetesNamespaceConverter.apiToNamespace(raw, yaml);
} catch (err) {
throw new PortainerError('Unable to retrieve namespace', err);
}
}
async getAllAsync() {
try {
const data = await this.KubernetesNamespaces().get().$promise;
const promises = _.map(data.items, (item) => this.KubernetesNamespaces().status({ id: item.metadata.name }).$promise);
const namespaces = await $allSettled(promises);
const visibleNamespaces = _.map(namespaces.fulfilled, (item) => {
if (item.status.phase !== 'Terminating') {
return KubernetesNamespaceConverter.apiToNamespace(item);
}
});
return _.without(visibleNamespaces, undefined);
} catch (err) {
throw new PortainerError('Unable to retrieve namespaces', err);
}
}
get(name) {
if (name) {
return this.$async(this.getAsync, name);
}
return this.$async(this.getAllAsync);
}
/**
* CREATE
*/
async createAsync(namespace) {
try {
const payload = KubernetesNamespaceConverter.createPayload(namespace);
const params = {};
const data = await this.KubernetesNamespaces().create(params, payload).$promise;
return data;
} catch (err) {
throw new PortainerError('Unable to create namespace', err);
}
}
create(namespace) {
return this.$async(this.createAsync, namespace);
}
/**
* DELETE
*/
async deleteAsync(namespace) {
try {
const params = new KubernetesCommonParams();
params.id = namespace.Name;
await this.KubernetesNamespaces().delete(params).$promise;
} catch (err) {
throw new PortainerError('Unable to delete namespace', err);
}
}
delete(namespace) {
return this.$async(this.deleteAsync, namespace);
}
}
export default KubernetesNamespaceService;
angular.module('portainer.kubernetes').service('KubernetesNamespaceService', KubernetesNamespaceService);

View file

@ -0,0 +1,50 @@
import angular from 'angular';
import _ from 'lodash-es';
import PortainerError from 'Portainer/error';
import KubernetesNodeConverter from 'Kubernetes/converters/node';
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
class KubernetesNodeService {
/* @ngInject */
constructor($async, KubernetesNodes) {
this.$async = $async;
this.KubernetesNodes = KubernetesNodes;
this.getAsync = this.getAsync.bind(this);
this.getAllAsync = this.getAllAsync.bind(this);
}
/**
* GET
*/
async getAsync(name) {
try {
const params = new KubernetesCommonParams();
params.id = name;
const [details, yaml] = await Promise.all([this.KubernetesNodes().get(params).$promise, this.KubernetesNodes().getYaml(params).$promise]);
return KubernetesNodeConverter.apiToNodeDetails(details, yaml);
} catch (err) {
throw new PortainerError('Unable to retrieve node details', err);
}
}
async getAllAsync() {
try {
const data = await this.KubernetesNodes().get().$promise;
return _.map(data.items, (item) => KubernetesNodeConverter.apiToNode(item));
} catch (err) {
throw { msg: 'Unable to retrieve nodes', err: err };
}
}
get(name) {
if (name) {
return this.$async(this.getAsync, name);
}
return this.$async(this.getAllAsync);
}
}
export default KubernetesNodeService;
angular.module('portainer.kubernetes').service('KubernetesNodeService', KubernetesNodeService);

View file

@ -0,0 +1,115 @@
import angular from 'angular';
import _ from 'lodash-es';
import PortainerError from 'Portainer/error';
import KubernetesPersistentVolumeClaimConverter from 'Kubernetes/converters/persistentVolumeClaim';
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
class KubernetesPersistentVolumeClaimService {
/* @ngInject */
constructor($async, EndpointProvider, KubernetesPersistentVolumeClaims) {
this.$async = $async;
this.EndpointProvider = EndpointProvider;
this.KubernetesPersistentVolumeClaims = KubernetesPersistentVolumeClaims;
this.getAsync = this.getAsync.bind(this);
this.getAllAsync = this.getAllAsync.bind(this);
this.createAsync = this.createAsync.bind(this);
this.patchAsync = this.patchAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
async getAsync(namespace, name) {
try {
const params = new KubernetesCommonParams();
params.id = name;
const [raw, yaml] = await Promise.all([
this.KubernetesPersistentVolumeClaims(namespace).get(params).$promise,
this.KubernetesPersistentVolumeClaims(namespace).getYaml(params).$promise,
]);
const storageClasses = this.EndpointProvider.currentEndpoint().Kubernetes.Configuration.StorageClasses;
return KubernetesPersistentVolumeClaimConverter.apiToPersistentVolumeClaim(raw, storageClasses, yaml);
} catch (err) {
throw new PortainerError('Unable to retrieve persistent volume claim', err);
}
}
async getAllAsync(namespace) {
try {
const data = await this.KubernetesPersistentVolumeClaims(namespace).get().$promise;
const storageClasses = this.EndpointProvider.currentEndpoint().Kubernetes.Configuration.StorageClasses;
return _.map(data.items, (item) => KubernetesPersistentVolumeClaimConverter.apiToPersistentVolumeClaim(item, storageClasses));
} catch (err) {
throw new PortainerError('Unable to retrieve persistent volume claims', err);
}
}
get(namespace, name) {
if (name) {
return this.$async(this.getAsync, namespace, name);
}
return this.$async(this.getAllAsync, namespace);
}
/**
* CREATE
*/
async createAsync(claim) {
try {
const params = {};
const payload = KubernetesPersistentVolumeClaimConverter.createPayload(claim);
const namespace = payload.metadata.namespace;
const data = await this.KubernetesPersistentVolumeClaims(namespace).create(params, payload).$promise;
return data;
} catch (err) {
throw new PortainerError('Unable to create persistent volume claim', err);
}
}
create(claim) {
return this.$async(this.createAsync, claim);
}
/**
* PATCH
*/
async patchAsync(oldPVC, newPVC) {
try {
const params = new KubernetesCommonParams();
params.id = newPVC.Name;
const namespace = newPVC.Namespace;
const payload = KubernetesPersistentVolumeClaimConverter.patchPayload(oldPVC, newPVC);
if (!payload.length) {
return;
}
const data = await this.KubernetesPersistentVolumeClaims(namespace).patch(params, payload).$promise;
return data;
} catch (err) {
throw new PortainerError('Unable to patch persistent volume claim', err);
}
}
patch(oldPVC, newPVC) {
return this.$async(this.patchAsync, oldPVC, newPVC);
}
/**
* DELETE
*/
async deleteAsync(pvc) {
try {
const params = new KubernetesCommonParams();
params.id = pvc.Name;
const namespace = pvc.Namespace;
await this.KubernetesPersistentVolumeClaims(namespace).delete(params).$promise;
} catch (err) {
throw new PortainerError('Unable to delete persistent volume claim', err);
}
}
delete(pvc) {
return this.$async(this.deleteAsync, pvc);
}
}
export default KubernetesPersistentVolumeClaimService;
angular.module('portainer.kubernetes').service('KubernetesPersistentVolumeClaimService', KubernetesPersistentVolumeClaimService);

View file

@ -0,0 +1,75 @@
import _ from 'lodash-es';
import angular from 'angular';
import PortainerError from 'Portainer/error';
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
import KubernetesPodConverter from 'Kubernetes/converters/pod';
class KubernetesPodService {
/* @ngInject */
constructor($async, KubernetesPods) {
this.$async = $async;
this.KubernetesPods = KubernetesPods;
this.getAllAsync = this.getAllAsync.bind(this);
this.logsAsync = this.logsAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
/**
* GET ALL
*/
async getAllAsync(namespace) {
try {
const data = await this.KubernetesPods(namespace).get().$promise;
return _.map(data.items, (item) => KubernetesPodConverter.apiToPod(item));
} catch (err) {
throw new PortainerError('Unable to retrieve pods', err);
}
}
get(namespace) {
return this.$async(this.getAllAsync, namespace);
}
/**
* Logs
*
* @param {string} namespace
* @param {string} podName
*/
async logsAsync(namespace, podName) {
try {
const params = new KubernetesCommonParams();
params.id = podName;
const data = await this.KubernetesPods(namespace).logs(params).$promise;
return data.logs.length === 0 ? [] : data.logs.split('\n');
} catch (err) {
throw new PortainerError('Unable to retrieve pod logs', err);
}
}
logs(namespace, podName) {
return this.$async(this.logsAsync, namespace, podName);
}
/**
* DELETE
*/
async deleteAsync(pod) {
try {
const params = new KubernetesCommonParams();
params.id = pod.Name;
const namespace = pod.Namespace;
await this.KubernetesPods(namespace).delete(params).$promise;
} catch (err) {
throw new PortainerError('Unable to remove pod', err);
}
}
delete(pod) {
return this.$async(this.deleteAsync, pod);
}
}
export default KubernetesPodService;
angular.module('portainer.kubernetes').service('KubernetesPodService', KubernetesPodService);

View file

@ -0,0 +1,31 @@
import angular from 'angular';
import PortainerError from 'Portainer/error';
class KubernetesReplicaSetService {
/* @ngInject */
constructor($async, KubernetesReplicaSets) {
this.$async = $async;
this.KubernetesReplicaSets = KubernetesReplicaSets;
this.getAllAsync = this.getAllAsync.bind(this);
}
/**
* GET
*/
async getAllAsync(namespace) {
try {
const data = await this.KubernetesReplicaSets(namespace).get().$promise;
return data.items;
} catch (err) {
throw new PortainerError('Unable to retrieve ReplicaSets', err);
}
}
get(namespace) {
return this.$async(this.getAllAsync, namespace);
}
}
export default KubernetesReplicaSetService;
angular.module('portainer.kubernetes').service('KubernetesReplicaSetService', KubernetesReplicaSetService);

View file

@ -0,0 +1,113 @@
import _ from 'lodash-es';
import { KubernetesResourceQuota } from 'Kubernetes/models/resource-quota/models';
import angular from 'angular';
import KubernetesResourcePoolConverter from 'Kubernetes/converters/resourcePool';
import KubernetesResourceQuotaHelper from 'Kubernetes/helpers/resourceQuotaHelper';
import { KubernetesNamespace } from 'Kubernetes/models/namespace/models';
class KubernetesResourcePoolService {
/* @ngInject */
constructor($async, KubernetesNamespaceService, KubernetesResourceQuotaService) {
this.$async = $async;
this.KubernetesNamespaceService = KubernetesNamespaceService;
this.KubernetesResourceQuotaService = KubernetesResourceQuotaService;
this.getAsync = this.getAsync.bind(this);
this.getAllAsync = this.getAllAsync.bind(this);
this.createAsync = this.createAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
/**
* GET
*/
async getAsync(name) {
try {
const namespace = await this.KubernetesNamespaceService.get(name);
const [quotaAttempt] = await Promise.allSettled([this.KubernetesResourceQuotaService.get(name, KubernetesResourceQuotaHelper.generateResourceQuotaName(name))]);
const pool = KubernetesResourcePoolConverter.apiToResourcePool(namespace);
if (quotaAttempt.status === 'fulfilled') {
pool.Quota = quotaAttempt.value;
pool.Yaml += '---\n' + quotaAttempt.value.Yaml;
}
return pool;
} catch (err) {
throw err;
}
}
async getAllAsync() {
try {
const namespaces = await this.KubernetesNamespaceService.get();
const pools = await Promise.all(
_.map(namespaces, async (namespace) => {
const name = namespace.Name;
const [quotaAttempt] = await Promise.allSettled([this.KubernetesResourceQuotaService.get(name, KubernetesResourceQuotaHelper.generateResourceQuotaName(name))]);
const pool = KubernetesResourcePoolConverter.apiToResourcePool(namespace);
if (quotaAttempt.status === 'fulfilled') {
pool.Quota = quotaAttempt.value;
pool.Yaml += '---\n' + quotaAttempt.value.Yaml;
}
return pool;
})
);
return pools;
} catch (err) {
throw err;
}
}
get(name) {
if (name) {
return this.$async(this.getAsync, name);
}
return this.$async(this.getAllAsync);
}
/**
* CREATE
*/
// TODO: review LimitRange future
async createAsync(name, owner, hasQuota, cpuLimit, memoryLimit) {
try {
const namespace = new KubernetesNamespace();
namespace.Name = name;
namespace.ResourcePoolName = name;
namespace.ResourcePoolOwner = owner;
await this.KubernetesNamespaceService.create(namespace);
if (hasQuota) {
const quota = new KubernetesResourceQuota(name);
quota.CpuLimit = cpuLimit;
quota.MemoryLimit = memoryLimit;
quota.ResourcePoolName = name;
quota.ResourcePoolOwner = owner;
await this.KubernetesResourceQuotaService.create(quota);
}
} catch (err) {
throw err;
}
}
create(name, owner, hasQuota, cpuLimit, memoryLimit) {
return this.$async(this.createAsync, name, owner, hasQuota, cpuLimit, memoryLimit);
}
/**
* DELETE
*/
async deleteAsync(pool) {
try {
await this.KubernetesNamespaceService.delete(pool.Namespace);
} catch (err) {
throw err;
}
}
delete(pool) {
return this.$async(this.deleteAsync, pool);
}
}
export default KubernetesResourcePoolService;
angular.module('portainer.kubernetes').service('KubernetesResourcePoolService', KubernetesResourcePoolService);

View file

@ -0,0 +1,109 @@
import _ from 'lodash-es';
import angular from 'angular';
import PortainerError from 'Portainer/error';
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
import KubernetesResourceQuotaConverter from 'Kubernetes/converters/resourceQuota';
class KubernetesResourceQuotaService {
/* @ngInject */
constructor($async, KubernetesResourceQuotas) {
this.$async = $async;
this.KubernetesResourceQuotas = KubernetesResourceQuotas;
this.getAsync = this.getAsync.bind(this);
this.getAllAsync = this.getAllAsync.bind(this);
this.createAsync = this.createAsync.bind(this);
this.updateAsync = this.updateAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
/**
* GET
*/
async getAsync(namespace, name) {
try {
const params = new KubernetesCommonParams();
params.id = name;
const [raw, yaml] = await Promise.all([this.KubernetesResourceQuotas(namespace).get(params).$promise, this.KubernetesResourceQuotas(namespace).getYaml(params).$promise]);
return KubernetesResourceQuotaConverter.apiToResourceQuota(raw, yaml);
} catch (err) {
throw new PortainerError('Unable to retrieve resource quota', err);
}
}
async getAllAsync(namespace) {
try {
const data = await this.KubernetesResourceQuotas(namespace).get().$promise;
return _.map(data.items, (item) => KubernetesResourceQuotaConverter.apiToResourceQuota(item));
} catch (err) {
throw new PortainerError('Unable to retrieve resource quotas', err);
}
}
get(namespace, name) {
if (name) {
return this.$async(this.getAsync, namespace, name);
}
return this.$async(this.getAllAsync, namespace);
}
/**
* CREATE
*/
async createAsync(quota) {
try {
const payload = KubernetesResourceQuotaConverter.createPayload(quota);
const namespace = payload.metadata.namespace;
const params = {};
const data = await this.KubernetesResourceQuotas(namespace).create(params, payload).$promise;
return KubernetesResourceQuotaConverter.apiToResourceQuota(data);
} catch (err) {
throw new PortainerError('Unable to create quota', err);
}
}
create(quota) {
return this.$async(this.createAsync, quota);
}
/**
* UPDATE
*/
async updateAsync(quota) {
try {
const payload = KubernetesResourceQuotaConverter.updatePayload(quota);
const params = new KubernetesCommonParams();
params.id = payload.metadata.name;
const namespace = payload.metadata.namespace;
const data = await this.KubernetesResourceQuotas(namespace).update(params, payload).$promise;
return data;
} catch (err) {
throw new PortainerError('Unable to update resource quota', err);
}
}
update(quota) {
return this.$async(this.updateAsync, quota);
}
/**
* DELETE
*/
async deleteAsync(quota) {
try {
const params = new KubernetesCommonParams();
params.id = quota.Name;
await this.KubernetesResourceQuotas(quota.Namespace).delete(params).$promise;
} catch (err) {
throw new PortainerError('Unable to delete quota', err);
}
}
delete(quota) {
return this.$async(this.deleteAsync, quota);
}
}
export default KubernetesResourceQuotaService;
angular.module('portainer.kubernetes').service('KubernetesResourceQuotaService', KubernetesResourceQuotaService);

View file

@ -0,0 +1,110 @@
import angular from 'angular';
import _ from 'lodash-es';
import PortainerError from 'Portainer/error';
import KubernetesSecretConverter from 'Kubernetes/converters/secret';
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
class KubernetesSecretService {
/* @ngInject */
constructor($async, KubernetesSecrets) {
this.$async = $async;
this.KubernetesSecrets = KubernetesSecrets;
this.getAsync = this.getAsync.bind(this);
this.getAllAsync = this.getAllAsync.bind(this);
this.createAsync = this.createAsync.bind(this);
this.updateAsync = this.updateAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
/**
* GET
*/
async getAsync(namespace, name) {
try {
const params = new KubernetesCommonParams();
params.id = name;
const [raw, yaml] = await Promise.all([this.KubernetesSecrets(namespace).get(params).$promise, this.KubernetesSecrets(namespace).getYaml(params).$promise]);
const secret = KubernetesSecretConverter.apiToSecret(raw, yaml);
return secret;
} catch (err) {
throw new PortainerError('Unable to retrieve secret', err);
}
}
async getAllAsync(namespace) {
try {
const data = await this.KubernetesSecrets(namespace).get().$promise;
return _.map(data.items, (item) => KubernetesSecretConverter.apiToSecret(item));
} catch (err) {
throw new PortainerError('Unable to retrieve secrets', err);
}
}
get(namespace, name) {
if (name) {
return this.$async(this.getAsync, namespace, name);
}
return this.$async(this.getAllAsync, namespace);
}
/**
* CREATE
*/
async createAsync(secret) {
try {
const payload = KubernetesSecretConverter.createPayload(secret);
const namespace = payload.metadata.namespace;
const params = {};
const data = await this.KubernetesSecrets(namespace).create(params, payload).$promise;
return data;
} catch (err) {
throw new PortainerError('Unable to create secret', err);
}
}
create(secret) {
return this.$async(this.createAsync, secret);
}
/**
* UPDATE
*/
async updateAsync(secret) {
try {
const payload = KubernetesSecretConverter.updatePayload(secret);
const params = new KubernetesCommonParams();
params.id = payload.metadata.name;
const namespace = payload.metadata.namespace;
const data = await this.KubernetesSecrets(namespace).update(params, payload).$promise;
return KubernetesSecretConverter.apiToSecret(data);
} catch (err) {
throw new PortainerError('Unable to update secret', err);
}
}
update(secret) {
return this.$async(this.updateAsync, secret);
}
/**
* DELETE
*/
async deleteAsync(secret) {
try {
const params = new KubernetesCommonParams();
params.id = secret.Name;
const namespace = secret.Namespace;
await this.KubernetesSecrets(namespace).delete(params).$promise;
} catch (err) {
throw new PortainerError('Unable to delete secret', err);
}
}
delete(secret) {
return this.$async(this.deleteAsync, secret);
}
}
export default KubernetesSecretService;
angular.module('portainer.kubernetes').service('KubernetesSecretService', KubernetesSecretService);

View file

@ -0,0 +1,115 @@
import angular from 'angular';
import PortainerError from 'Portainer/error';
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
import KubernetesServiceConverter from 'Kubernetes/converters/service';
class KubernetesServiceService {
/* @ngInject */
constructor($async, KubernetesServices) {
this.$async = $async;
this.KubernetesServices = KubernetesServices;
this.getAsync = this.getAsync.bind(this);
this.getAllAsync = this.getAllAsync.bind(this);
this.createAsync = this.createAsync.bind(this);
this.patchAsync = this.patchAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
/**
* GET
*/
async getAsync(namespace, name) {
try {
const params = new KubernetesCommonParams();
params.id = name;
const [raw, yaml] = await Promise.all([this.KubernetesServices(namespace).get(params).$promise, this.KubernetesServices(namespace).getYaml(params).$promise]);
const res = {
Raw: raw,
Yaml: yaml.data,
};
return res;
} catch (err) {
throw new PortainerError('Unable to retrieve service', err);
}
}
async getAllAsync(namespace) {
try {
const data = await this.KubernetesServices(namespace).get().$promise;
return data.items;
} catch (err) {
throw new PortainerError('Unable to retrieve services', err);
}
}
get(namespace, name) {
if (name) {
return this.$async(this.getAsync, namespace, name);
}
return this.$async(this.getAllAsync, namespace);
}
/**
* CREATE
*/
async createAsync(service) {
try {
const params = {};
const payload = KubernetesServiceConverter.createPayload(service);
const namespace = payload.metadata.namespace;
const data = await this.KubernetesServices(namespace).create(params, payload).$promise;
return data;
} catch (err) {
throw new PortainerError('Unable to create service', err);
}
}
create(service) {
return this.$async(this.createAsync, service);
}
/**
* PATCH
*/
async patchAsync(oldService, newService) {
try {
const params = new KubernetesCommonParams();
params.id = newService.Name;
const namespace = newService.Namespace;
const payload = KubernetesServiceConverter.patchPayload(oldService, newService);
if (!payload.length) {
return;
}
const data = await this.KubernetesServices(namespace).patch(params, payload).$promise;
return data;
} catch (err) {
throw new PortainerError('Unable to patch service', err);
}
}
patch(oldService, newService) {
return this.$async(this.patchAsync, oldService, newService);
}
/**
* DELETE
*/
async deleteAsync(service) {
try {
const params = new KubernetesCommonParams();
params.id = service.Name;
const namespace = service.Namespace;
await this.KubernetesServices(namespace).delete(params).$promise;
} catch (err) {
throw new PortainerError('Unable to remove service', err);
}
}
delete(service) {
return this.$async(this.deleteAsync, service);
}
}
export default KubernetesServiceService;
angular.module('portainer.kubernetes').service('KubernetesServiceService', KubernetesServiceService);

View file

@ -0,0 +1,32 @@
import _ from 'lodash-es';
import angular from 'angular';
class KubernetesStackService {
/* @ngInject */
constructor($async, KubernetesApplicationService) {
this.$async = $async;
this.KubernetesApplicationService = KubernetesApplicationService;
this.getAllAsync = this.getAllAsync.bind(this);
}
/**
* GET
*/
async getAllAsync(namespace) {
try {
const applications = await this.KubernetesApplicationService.get(namespace);
const stacks = _.map(applications, (item) => item.StackName);
return _.uniq(_.without(stacks, '-'));
} catch (err) {
throw err;
}
}
get(namespace) {
return this.$async(this.getAllAsync, namespace);
}
}
export default KubernetesStackService;
angular.module('portainer.kubernetes').service('KubernetesStackService', KubernetesStackService);

View file

@ -0,0 +1,144 @@
import angular from 'angular';
import PortainerError from 'Portainer/error';
import { KubernetesCommonParams } from 'Kubernetes/models/common/params';
import KubernetesStatefulSetConverter from 'Kubernetes/converters/statefulSet';
class KubernetesStatefulSetService {
/* @ngInject */
constructor($async, KubernetesStatefulSets, KubernetesServiceService) {
this.$async = $async;
this.KubernetesStatefulSets = KubernetesStatefulSets;
this.KubernetesServiceService = KubernetesServiceService;
this.getAsync = this.getAsync.bind(this);
this.getAllAsync = this.getAllAsync.bind(this);
this.createAsync = this.createAsync.bind(this);
this.patchAsync = this.patchAsync.bind(this);
this.rollbackAsync = this.rollbackAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
/**
* GET
*/
async getAsync(namespace, name) {
try {
const params = new KubernetesCommonParams();
params.id = name;
const [raw, yaml] = await Promise.all([this.KubernetesStatefulSets(namespace).get(params).$promise, this.KubernetesStatefulSets(namespace).getYaml(params).$promise]);
const res = {
Raw: raw,
Yaml: yaml.data,
};
const headlessServiceName = raw.spec.serviceName;
if (headlessServiceName) {
try {
const headlessService = await this.KubernetesServiceService.get(namespace, headlessServiceName);
res.Yaml += '---\n' + headlessService.Yaml;
} catch (error) {
// if has error means headless service does not exist
// skip error as we don't care in this case
}
}
return res;
} catch (err) {
throw new PortainerError('Unable to retrieve StatefulSet', err);
}
}
async getAllAsync(namespace) {
try {
const data = await this.KubernetesStatefulSets(namespace).get().$promise;
return data.items;
} catch (err) {
throw new PortainerError('Unable to retrieve StatefulSets', err);
}
}
get(namespace, name) {
if (name) {
return this.$async(this.getAsync, namespace, name);
}
return this.$async(this.getAllAsync, namespace);
}
/**
* CREATE
*/
async createAsync(statefulSet) {
try {
const params = {};
const payload = KubernetesStatefulSetConverter.createPayload(statefulSet);
const namespace = payload.metadata.namespace;
const data = await this.KubernetesStatefulSets(namespace).create(params, payload).$promise;
return data;
} catch (err) {
throw new PortainerError('Unable to create statefulSet', err);
}
}
create(statefulSet) {
return this.$async(this.createAsync, statefulSet);
}
/**
* PATCH
*/
async patchAsync(oldStatefulSet, newStatefulSet) {
try {
const params = new KubernetesCommonParams();
params.id = newStatefulSet.Name;
const namespace = newStatefulSet.Namespace;
const payload = KubernetesStatefulSetConverter.patchPayload(oldStatefulSet, newStatefulSet);
if (!payload.length) {
return;
}
const data = await this.KubernetesStatefulSets(namespace).patch(params, payload).$promise;
return data;
} catch (err) {
throw new PortainerError('Unable to patch statefulSet', err);
}
}
patch(oldStatefulSet, newStatefulSet) {
return this.$async(this.patchAsync, oldStatefulSet, newStatefulSet);
}
/**
* DELETE
*/
async deleteAsync(statefulSet) {
try {
const params = new KubernetesCommonParams();
params.id = statefulSet.Name;
const namespace = statefulSet.Namespace;
await this.KubernetesStatefulSets(namespace).delete(params).$promise;
} catch (err) {
throw new PortainerError('Unable to remove statefulSet', err);
}
}
delete(statefulSet) {
return this.$async(this.deleteAsync, statefulSet);
}
/**
* ROLLBACK
*/
async rollbackAsync(namespace, name, payload) {
try {
const params = new KubernetesCommonParams();
params.id = name;
await this.KubernetesStatefulSets(namespace).rollback(params, payload).$promise;
} catch (err) {
throw new PortainerError('Unable to rollback statefulSet', err);
}
}
rollback(namespace, name, payload) {
return this.$async(this.rollbackAsync, namespace, name, payload);
}
}
export default KubernetesStatefulSetService;
angular.module('portainer.kubernetes').service('KubernetesStatefulSetService', KubernetesStatefulSetService);

View file

@ -0,0 +1,37 @@
import angular from 'angular';
import _ from 'lodash-es';
import PortainerError from 'Portainer/error';
import KubernetesStorageClassConverter from 'Kubernetes/converters/storageClass';
class KubernetesStorageService {
/* @ngInject */
constructor($async, KubernetesStorage) {
this.$async = $async;
this.KubernetesStorage = KubernetesStorage;
this.getAsync = this.getAsync.bind(this);
}
/**
* GET
*/
async getAsync(endpointId) {
try {
const params = {
endpointId: endpointId,
};
const classes = await this.KubernetesStorage().get(params).$promise;
const res = _.map(classes.items, (item) => KubernetesStorageClassConverter.apiToStorageClass(item));
return res;
} catch (err) {
throw new PortainerError('Unable to retrieve storage classes', err);
}
}
get(endpointId) {
return this.$async(this.getAsync, endpointId);
}
}
export default KubernetesStorageService;
angular.module('portainer.kubernetes').service('KubernetesStorageService', KubernetesStorageService);

View file

@ -0,0 +1,70 @@
import angular from 'angular';
import _ from 'lodash-es';
import KubernetesVolumeConverter from 'Kubernetes/converters/volume';
class KubernetesVolumeService {
/* @ngInject */
constructor($async, KubernetesResourcePoolService, KubernetesApplicationService, KubernetesPersistentVolumeClaimService) {
this.$async = $async;
this.KubernetesResourcePoolService = KubernetesResourcePoolService;
this.KubernetesApplicationService = KubernetesApplicationService;
this.KubernetesPersistentVolumeClaimService = KubernetesPersistentVolumeClaimService;
this.getAsync = this.getAsync.bind(this);
this.getAllAsync = this.getAllAsync.bind(this);
this.deleteAsync = this.deleteAsync.bind(this);
}
/**
* GET
*/
async getAsync(namespace, name) {
try {
const [pvc, pool] = await Promise.all([await this.KubernetesPersistentVolumeClaimService.get(namespace, name), await this.KubernetesResourcePoolService.get(namespace)]);
return KubernetesVolumeConverter.pvcToVolume(pvc, pool);
} catch (err) {
throw err;
}
}
async getAllAsync(namespace) {
try {
const pools = await this.KubernetesResourcePoolService.get(namespace);
const res = await Promise.all(
_.map(pools, async (pool) => {
const pvcs = await this.KubernetesPersistentVolumeClaimService.get(pool.Namespace.Name);
return _.map(pvcs, (pvc) => KubernetesVolumeConverter.pvcToVolume(pvc, pool));
})
);
return _.flatten(res);
} catch (err) {
throw err;
}
}
get(namespace, name) {
if (name) {
return this.$async(this.getAsync, namespace, name);
}
return this.$async(this.getAllAsync, namespace);
}
/**
* DELETE
*/
async deleteAsync(volume) {
try {
await this.KubernetesPersistentVolumeClaimService.delete(volume.PersistentVolumeClaim);
} catch (err) {
throw err;
}
}
delete(volume) {
return this.$async(this.deleteAsync, volume);
}
}
export default KubernetesVolumeService;
angular.module('portainer.kubernetes').service('KubernetesVolumeService', KubernetesVolumeService);