mirror of
https://github.com/portainer/portainer.git
synced 2025-08-08 15:25:22 +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:
parent
24528ecea8
commit
af6bea5acc
361 changed files with 20794 additions and 2349 deletions
|
@ -8,7 +8,7 @@ async function initAuthentication(authManager, Authentication, $rootScope, $stat
|
|||
// to have more controls on which URL should trigger the unauthenticated state.
|
||||
$rootScope.$on('unauthenticated', function (event, data) {
|
||||
if (!_.includes(data.config.url, '/v2/') && !_.includes(data.config.url, '/api/v4/')) {
|
||||
$state.go('portainer.auth', { error: 'Your session has expired' });
|
||||
$state.go('portainer.logout', { error: 'Your session has expired' });
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -106,8 +106,7 @@ angular.module('portainer.app', []).config([
|
|||
name: 'portainer.auth',
|
||||
url: '/auth',
|
||||
params: {
|
||||
logout: false,
|
||||
error: '',
|
||||
reload: false,
|
||||
},
|
||||
views: {
|
||||
'content@': {
|
||||
|
@ -118,6 +117,22 @@ angular.module('portainer.app', []).config([
|
|||
'sidebar@': {},
|
||||
},
|
||||
};
|
||||
const logout = {
|
||||
name: 'portainer.logout',
|
||||
url: '/logout',
|
||||
params: {
|
||||
error: '',
|
||||
performApiLogout: false,
|
||||
},
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: './views/logout/logout.html',
|
||||
controller: 'LogoutController',
|
||||
controllerAs: 'ctrl',
|
||||
},
|
||||
'sidebar@': {},
|
||||
},
|
||||
};
|
||||
|
||||
var endpoints = {
|
||||
name: 'portainer.endpoints',
|
||||
|
@ -141,6 +156,18 @@ angular.module('portainer.app', []).config([
|
|||
},
|
||||
};
|
||||
|
||||
const endpointKubernetesConfiguration = {
|
||||
name: 'portainer.endpoints.endpoint.kubernetesConfig',
|
||||
url: '/configure',
|
||||
views: {
|
||||
'content@': {
|
||||
templateUrl: '../kubernetes/views/configure/configure.html',
|
||||
controller: 'KubernetesConfigureController',
|
||||
controllerAs: 'ctrl',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var endpointCreation = {
|
||||
name: 'portainer.endpoints.new',
|
||||
url: '/new',
|
||||
|
@ -235,6 +262,7 @@ angular.module('portainer.app', []).config([
|
|||
'content@': {
|
||||
templateUrl: './views/init/endpoint/initEndpoint.html',
|
||||
controller: 'InitEndpointController',
|
||||
controllerAs: 'ctrl',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
@ -491,10 +519,12 @@ angular.module('portainer.app', []).config([
|
|||
$stateRegistryProvider.register(about);
|
||||
$stateRegistryProvider.register(account);
|
||||
$stateRegistryProvider.register(authentication);
|
||||
$stateRegistryProvider.register(logout);
|
||||
$stateRegistryProvider.register(endpoints);
|
||||
$stateRegistryProvider.register(endpoint);
|
||||
$stateRegistryProvider.register(endpointAccess);
|
||||
$stateRegistryProvider.register(endpointCreation);
|
||||
$stateRegistryProvider.register(endpointKubernetesConfiguration);
|
||||
$stateRegistryProvider.register(groups);
|
||||
$stateRegistryProvider.register(group);
|
||||
$stateRegistryProvider.register(groupAccess);
|
||||
|
|
|
@ -69,12 +69,14 @@
|
|||
>
|
||||
<td>
|
||||
<span class="md-checkbox">
|
||||
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-disabled="item.Inherited" ng-click="$ctrl.selectItem(item, $event)" />
|
||||
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-disabled="$ctrl.disableRemove(item)" ng-click="$ctrl.selectItem(item, $event)" />
|
||||
<label for="select_{{ $index }}"></label>
|
||||
</span>
|
||||
{{ item.Name }}
|
||||
<span ng-if="item.Inherited" class="text-muted small" style="margin-left: 2px;"><code style="font-size: 85% !important;">inherited</code></span>
|
||||
<span ng-if="item.Override" class="text-muted small" style="margin-left: 2px;"><code style="font-size: 85% !important;">override</code></span>
|
||||
<span ng-if="$ctrl.inheritFrom && item.Inherited" class="text-muted small" style="margin-left: 2px;"
|
||||
><code style="font-size: 85% !important;">inherited</code></span
|
||||
>
|
||||
<span ng-if="$ctrl.inheritFrom && item.Override" class="text-muted small" style="margin-left: 2px;"><code style="font-size: 85% !important;">override</code></span>
|
||||
</td>
|
||||
<td>{{ item.Type }}</td>
|
||||
<td ng-if="$ctrl.rbacEnabled">
|
||||
|
|
|
@ -6,7 +6,7 @@ angular.module('portainer.app').controller('AccessDatatableController', [
|
|||
angular.extend(this, $controller('GenericDatatableController', { $scope: $scope }));
|
||||
|
||||
this.disableRemove = function (item) {
|
||||
return item.Inherited;
|
||||
return item.Inherited && this.inheritFrom;
|
||||
};
|
||||
|
||||
this.allowSelection = function (item) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import _ from 'lodash-es';
|
||||
import { ResourceControlOwnership as RCO } from 'Portainer/models/resourceControl/resourceControlOwnership';
|
||||
import { ResourceControlTypeString as RCTS, ResourceControlTypeInt as RCTI } from 'Portainer/models/resourceControl/resourceControlTypes';
|
||||
import { ResourceControlTypeInt as RCTI, ResourceControlTypeString as RCTS } from 'Portainer/models/resourceControl/resourceControlTypes';
|
||||
import { AccessControlPanelData } from './porAccessControlPanelModel';
|
||||
|
||||
angular.module('portainer.app').controller('porAccessControlPanelController', [
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-if="ctrl.entityType !== 'registry'">
|
||||
<label class="col-sm-3 col-lg-2 control-label text-left">
|
||||
Role
|
||||
|
@ -39,7 +38,6 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- actions -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
|
|
|
@ -53,27 +53,14 @@ class PorAccessManagementController {
|
|||
}
|
||||
|
||||
async $onInit() {
|
||||
const entity = this.accessControlledEntity;
|
||||
if (!entity) {
|
||||
this.Notifications.error('Failure', 'Unable to retrieve accesses');
|
||||
return;
|
||||
}
|
||||
if (!entity.UserAccessPolicies) {
|
||||
entity.UserAccessPolicies = {};
|
||||
}
|
||||
if (!entity.TeamAccessPolicies) {
|
||||
entity.TeamAccessPolicies = {};
|
||||
}
|
||||
const parent = this.inheritFrom;
|
||||
if (parent && !parent.UserAccessPolicies) {
|
||||
parent.UserAccessPolicies = {};
|
||||
}
|
||||
if (parent && !parent.TeamAccessPolicies) {
|
||||
parent.TeamAccessPolicies = {};
|
||||
}
|
||||
this.roles = [];
|
||||
this.rbacEnabled = false;
|
||||
try {
|
||||
const entity = this.accessControlledEntity;
|
||||
const parent = this.inheritFrom;
|
||||
// TODO: refactor
|
||||
// extract this code and locate it in AccessService.accesses() function
|
||||
// see resourcePoolAccessController for another usage of AccessService.accesses()
|
||||
// which needs RBAC support
|
||||
this.roles = [];
|
||||
this.rbacEnabled = await this.ExtensionService.extensionEnabled(this.ExtensionService.EXTENSIONS.RBAC);
|
||||
if (this.rbacEnabled) {
|
||||
this.roles = await this.RoleService.roles();
|
||||
|
@ -81,13 +68,7 @@ class PorAccessManagementController {
|
|||
selectedRole: this.roles[0],
|
||||
};
|
||||
}
|
||||
const data = await this.AccessService.accesses(
|
||||
entity.UserAccessPolicies,
|
||||
entity.TeamAccessPolicies,
|
||||
parent ? parent.UserAccessPolicies : {},
|
||||
parent ? parent.TeamAccessPolicies : {},
|
||||
this.roles
|
||||
);
|
||||
const data = await this.AccessService.accesses(entity, parent, this.roles);
|
||||
this.availableUsersAndTeams = _.orderBy(data.availableUsersAndTeams, 'Name', 'asc');
|
||||
this.authorizedUsersAndTeams = data.authorizedUsersAndTeams;
|
||||
} catch (err) {
|
||||
|
|
|
@ -6,6 +6,7 @@ function isBetween(value, a, b) {
|
|||
return (value >= a && value <= b) || (value >= b && value <= a);
|
||||
}
|
||||
|
||||
// TODO: review - refactor to use a class that can be extended
|
||||
angular.module('portainer.app').controller('GenericDatatableController', [
|
||||
'$interval',
|
||||
'PaginationService',
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
<div class="blocklist-item" ng-click="$ctrl.onSelect($ctrl.model)">
|
||||
<div class="blocklist-item-box">
|
||||
<span ng-class="['blocklist-item-logo', 'endpoint-item', { azure: $ctrl.model.Type === 3 }]">
|
||||
<i ng-if="$ctrl.model.Type !== 4" ng-class="$ctrl.model.Type | endpointtypeicon" class="fa-4x blue-icon" aria-hidden="true"></i>
|
||||
<i
|
||||
ng-if="$ctrl.model.Type !== 4 && $ctrl.model.Type !== 5 && $ctrl.model.Type !== 6 && $ctrl.model.Type !== 7"
|
||||
ng-class="$ctrl.model.Type | endpointtypeicon"
|
||||
class="fa-4x blue-icon"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
<img ng-if="$ctrl.model.Type === 4" src="~@/assets/images/edge_endpoint.png" />
|
||||
<img ng-if="$ctrl.model.Type === 5 || $ctrl.model.Type === 6 || $ctrl.model.Type === 7" src="~@/assets/images/kubernetes_endpoint.png" />
|
||||
</span>
|
||||
|
||||
<span class="col-sm-12">
|
||||
|
@ -22,6 +28,9 @@
|
|||
<span class="space-left small text-muted" ng-if="$ctrl.model.Snapshots[0]">
|
||||
{{ $ctrl.model.Snapshots[0].Time | getisodatefromtimestamp }}
|
||||
</span>
|
||||
<span class="space-left small text-muted" ng-if="$ctrl.model.Kubernetes.Snapshots[0]">
|
||||
{{ $ctrl.model.Kubernetes.Snapshots[0].Time | getisodatefromtimestamp }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
|
@ -69,12 +78,36 @@
|
|||
</span>
|
||||
</div>
|
||||
|
||||
<div class="blocklist-item-line endpoint-item" ng-if="!$ctrl.model.Snapshots[0]">
|
||||
<div class="blocklist-item-line endpoint-item" ng-if="!$ctrl.model.Snapshots[0] && $ctrl.model.Type !== 5 && $ctrl.model.Type !== 6 && $ctrl.model.Type !== 7">
|
||||
<span class="blocklist-item-desc">
|
||||
No snapshot available
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="blocklist-item-line endpoint-item" ng-if="$ctrl.model.Kubernetes.Snapshots[0] && ($ctrl.model.Type === 5 || $ctrl.model.Type === 6 || $ctrl.model.Type === 7)">
|
||||
<span class="blocklist-item-desc">
|
||||
<span>
|
||||
<span style="padding: 0 7px 0 0;"> <i class="fa fa-microchip space-right" aria-hidden="true"></i>{{ $ctrl.model.Kubernetes.Snapshots[0].TotalCPU }} CPU </span>
|
||||
<span style="padding: 0 7px 0 7px;">
|
||||
<i class="fa fa-memory space-right" aria-hidden="true"></i>{{ $ctrl.model.Kubernetes.Snapshots[0].TotalMemory | humansize }} RAM
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span class="small text-muted">
|
||||
Kubernetes {{ $ctrl.model.Kubernetes.Snapshots[0].KubernetesVersion }}
|
||||
<span style="padding: 0 0 0 7px;">
|
||||
<i class="fa fa-hdd space-left space-right" aria-hidden="true"></i>
|
||||
{{ $ctrl.model.Kubernetes.Snapshots[0].NodeCount }} {{ $ctrl.model.Kubernetes.Snapshots[0].NodeCount === 1 ? 'node' : 'nodes' }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="blocklist-item-line endpoint-item" ng-if="!$ctrl.model.Kubernetes.Snapshots[0] && ($ctrl.model.Type === 5 || $ctrl.model.Type === 6 || $ctrl.model.Type === 7)">
|
||||
<span class="blocklist-item-desc">
|
||||
-
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="blocklist-item-line endpoint-item">
|
||||
<span class="small text-muted">
|
||||
<span ng-if="$ctrl.model.Type === 1">
|
||||
|
|
|
@ -8,7 +8,7 @@ angular.module('portainer.app').directive('rdHeaderContent', [
|
|||
scope.username = Authentication.getUserDetails().username;
|
||||
},
|
||||
template:
|
||||
'<div class="breadcrumb-links"><div class="pull-left" ng-transclude></div><div class="pull-right" ng-if="username"><a ui-sref="portainer.account" style="margin-right: 5px;"><u><i class="fa fa-wrench" aria-hidden="true"></i> my account </u></a><a ui-sref="portainer.auth({logout: true})" class="text-danger" style="margin-right: 25px;"><u><i class="fa fa-sign-out-alt" aria-hidden="true"></i> log out</u></a></div></div>',
|
||||
'<div class="breadcrumb-links"><div class="pull-left" ng-transclude></div><div class="pull-right" ng-if="username"><a ui-sref="portainer.account" style="margin-right: 5px;"><u><i class="fa fa-wrench" aria-hidden="true"></i> my account </u></a><a ui-sref="portainer.logout({performApiLogout: true})" class="text-danger" style="margin-right: 25px;"><u><i class="fa fa-sign-out-alt" aria-hidden="true"></i> log out</u></a></div></div>',
|
||||
restrict: 'E',
|
||||
};
|
||||
return directive;
|
||||
|
|
|
@ -1,21 +1,45 @@
|
|||
angular.module('portainer.app').controller('SliderController', function () {
|
||||
var ctrl = this;
|
||||
// TODO: k8s merge - TEST WITH EXISTING SLIDERS !
|
||||
// Not sure if this is not breaking existing sliders on docker views
|
||||
// Or sliders with onChange call (docker service update view)
|
||||
import angular from 'angular';
|
||||
|
||||
ctrl.options = {
|
||||
floor: ctrl.floor,
|
||||
ceil: ctrl.ceil,
|
||||
step: ctrl.step,
|
||||
precision: ctrl.precision,
|
||||
showSelectionBar: true,
|
||||
enforceStep: false,
|
||||
translate: function (value, sliderId, label) {
|
||||
if ((label === 'floor' && ctrl.floor === 0) || value === 0) {
|
||||
return 'unlimited';
|
||||
}
|
||||
return value;
|
||||
},
|
||||
onChange: function () {
|
||||
ctrl.onChange();
|
||||
},
|
||||
};
|
||||
});
|
||||
class SliderController {
|
||||
/* @ngInject */
|
||||
constructor($scope) {
|
||||
this.$scope = $scope;
|
||||
|
||||
this.buildOptions = this.buildOptions.bind(this);
|
||||
this.translate = this.translate.bind(this);
|
||||
}
|
||||
|
||||
$onChanges() {
|
||||
this.buildOptions();
|
||||
}
|
||||
|
||||
translate(value, sliderId, label) {
|
||||
if ((label === 'floor' && this.floor === 0) || value === 0) {
|
||||
return 'unlimited';
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
buildOptions() {
|
||||
this.options = {
|
||||
floor: this.floor,
|
||||
ceil: this.ceil,
|
||||
step: this.step,
|
||||
precision: this.precision,
|
||||
showSelectionBar: true,
|
||||
enforceStep: false,
|
||||
translate: this.translate,
|
||||
onChange: () => this.onChange(),
|
||||
};
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
this.buildOptions();
|
||||
}
|
||||
}
|
||||
|
||||
export default SliderController;
|
||||
angular.module('portainer.app').controller('SliderController', SliderController);
|
||||
|
|
|
@ -4,9 +4,12 @@ angular.module('portainer.app').directive('portainerTooltip', [
|
|||
scope: {
|
||||
message: '@',
|
||||
position: '@',
|
||||
customStyle: '<?',
|
||||
},
|
||||
template:
|
||||
'<span class="interactive" tooltip-append-to-body="true" tooltip-placement="{{position}}" tooltip-class="portainer-tooltip" uib-tooltip="{{message}}"><i class="fa fa-question-circle tooltip-icon" aria-hidden="true"></i></span>',
|
||||
template: `
|
||||
<span class="interactive" tooltip-append-to-body="true" tooltip-placement="{{position}}" tooltip-class="portainer-tooltip" uib-tooltip="{{message}}">
|
||||
<i class="" ng-class="['fa fa-question-circle blue-icon', {'tooltip-icon': !customStyle}]" ng-style="customStyle" aria-hidden="true"></i>
|
||||
</span>`,
|
||||
restrict: 'E',
|
||||
};
|
||||
return directive;
|
||||
|
|
6
app/portainer/error.js
Normal file
6
app/portainer/error.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
export default class PortainerError {
|
||||
constructor(msg, err) {
|
||||
this.msg = msg;
|
||||
this.err = err;
|
||||
}
|
||||
}
|
|
@ -126,11 +126,13 @@ angular
|
|||
return function (type) {
|
||||
if (type === 1) {
|
||||
return 'Docker';
|
||||
} else if (type === 2) {
|
||||
} else if (type === 2 || type === 6) {
|
||||
return 'Agent';
|
||||
} else if (type === 3) {
|
||||
return 'Azure ACI';
|
||||
} else if (type === 4) {
|
||||
} else if (type === 5) {
|
||||
return 'Kubernetes';
|
||||
} else if (type === 4 || type === 7) {
|
||||
return 'Edge Agent';
|
||||
}
|
||||
return '';
|
||||
|
@ -143,6 +145,8 @@ angular
|
|||
return 'fab fa-microsoft';
|
||||
} else if (type === 4) {
|
||||
return 'fa fa-cloud';
|
||||
} else if (type === 5 || type === 6 || type === 7) {
|
||||
return 'fas fa-dharmachakra';
|
||||
}
|
||||
return 'fab fa-docker';
|
||||
};
|
||||
|
|
41
app/portainer/models/endpoint/formValues.js
Normal file
41
app/portainer/models/endpoint/formValues.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { PortainerEndpointConnectionTypes } from 'Portainer/models/endpoint/models';
|
||||
|
||||
export class PortainerEndpointInitFormValues {
|
||||
constructor() {
|
||||
this.ConnectionType = PortainerEndpointConnectionTypes.KUBERNETES_LOCAL;
|
||||
this.Name = '';
|
||||
this.URL = '';
|
||||
this.TLS = false;
|
||||
this.TLSSkipVerify = false;
|
||||
this.TLSSKipClientVerify = false;
|
||||
this.TLSCACert = null;
|
||||
this.TLSCert = null;
|
||||
this.TLSKey = null;
|
||||
this.AzureApplicationId = '';
|
||||
this.AzureTenantId = '';
|
||||
this.AzureAuthenticationKey = '';
|
||||
}
|
||||
}
|
||||
|
||||
class PortainerEndpointInitFormValueEndpointSection {
|
||||
constructor(value, title, classes, description) {
|
||||
this.Id = value;
|
||||
this.Value = value;
|
||||
this.Title = title;
|
||||
this.Classes = classes;
|
||||
this.Description = description;
|
||||
}
|
||||
}
|
||||
|
||||
export const PortainerEndpointInitFormValueEndpointSections = Object.freeze([
|
||||
new PortainerEndpointInitFormValueEndpointSection(PortainerEndpointConnectionTypes.DOCKER_LOCAL, 'Docker', 'fab fa-docker', 'Manage the local Docker environment'),
|
||||
new PortainerEndpointInitFormValueEndpointSection(
|
||||
PortainerEndpointConnectionTypes.KUBERNETES_LOCAL,
|
||||
'Kubernetes',
|
||||
'fas fa-dharmachakra',
|
||||
'Manage the local Kubernetes environment'
|
||||
),
|
||||
new PortainerEndpointInitFormValueEndpointSection(PortainerEndpointConnectionTypes.REMOTE, 'Remote', 'fab fa-docker', 'Manage a remote Docker environment'),
|
||||
new PortainerEndpointInitFormValueEndpointSection(PortainerEndpointConnectionTypes.AGENT, 'Agent', 'fa fa-bolt', 'Connect to a Portainer agent'),
|
||||
new PortainerEndpointInitFormValueEndpointSection(PortainerEndpointConnectionTypes.AZURE, 'Azure', 'fab fa-microsoft', 'Connect to Microsoft Azure ACI'),
|
||||
]);
|
28
app/portainer/models/endpoint/models.js
Normal file
28
app/portainer/models/endpoint/models.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* JS reference of portainer.go#EndpointType iota
|
||||
*/
|
||||
export const PortainerEndpointTypes = Object.freeze({
|
||||
// DockerEnvironment represents an endpoint connected to a Docker environment
|
||||
DockerEnvironment: 1,
|
||||
// AgentOnDockerEnvironment represents an endpoint connected to a Portainer agent deployed on a Docker environment
|
||||
AgentOnDockerEnvironment: 2,
|
||||
// AzureEnvironment represents an endpoint connected to an Azure environment
|
||||
AzureEnvironment: 3,
|
||||
// EdgeAgentOnDockerEnvironment represents an endpoint connected to an Edge agent deployed on a Docker environment
|
||||
EdgeAgentOnDockerEnvironment: 4,
|
||||
// KubernetesLocalEnvironment represents an endpoint connected to a local Kubernetes environment
|
||||
KubernetesLocalEnvironment: 5,
|
||||
// AgentOnKubernetesEnvironment represents an endpoint connected to a Portainer agent deployed on a Kubernetes environment
|
||||
AgentOnKubernetesEnvironment: 6,
|
||||
// EdgeAgentOnKubernetesEnvironment represents an endpoint connected to an Edge agent deployed on a Kubernetes environment
|
||||
EdgeAgentOnKubernetesEnvironment: 7,
|
||||
});
|
||||
|
||||
export const PortainerEndpointConnectionTypes = Object.freeze({
|
||||
DOCKER_LOCAL: 1,
|
||||
KUBERNETES_LOCAL: 2,
|
||||
REMOTE: 3,
|
||||
AZURE: 4,
|
||||
AGENT: 5,
|
||||
EDGE: 6,
|
||||
});
|
|
@ -4,13 +4,11 @@ angular.module('portainer.app').factory('Auth', [
|
|||
function AuthFactory($resource, API_ENDPOINT_AUTH) {
|
||||
'use strict';
|
||||
return $resource(
|
||||
API_ENDPOINT_AUTH,
|
||||
API_ENDPOINT_AUTH + '/:action',
|
||||
{},
|
||||
{
|
||||
login: {
|
||||
method: 'POST',
|
||||
ignoreLoadingBar: true,
|
||||
},
|
||||
login: { method: 'POST', ignoreLoadingBar: true },
|
||||
logout: { method: 'POST', params: { action: 'logout' }, ignoreLoadingBar: true },
|
||||
}
|
||||
);
|
||||
},
|
||||
|
|
34
app/portainer/services/allSettled.js
Normal file
34
app/portainer/services/allSettled.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
import _ from 'lodash-es';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {any[]} promises
|
||||
*/
|
||||
export default async function $allSettled(promises) {
|
||||
const res = {
|
||||
fulfilled: [],
|
||||
rejected: [],
|
||||
};
|
||||
const data = await Promise.allSettled(promises);
|
||||
res.fulfilled = _.reduce(
|
||||
data,
|
||||
(acc, item) => {
|
||||
if (item.status === 'fulfilled') {
|
||||
acc.push(item.value);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
);
|
||||
res.rejected = _.reduce(
|
||||
data,
|
||||
(acc, item) => {
|
||||
if (item.status === 'rejected') {
|
||||
acc.push(item.reason);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
);
|
||||
return res;
|
||||
}
|
|
@ -4,9 +4,10 @@ import { TeamAccessViewModel } from '../../models/access';
|
|||
|
||||
angular.module('portainer.app').factory('AccessService', [
|
||||
'$q',
|
||||
'$async',
|
||||
'UserService',
|
||||
'TeamService',
|
||||
function AccessServiceFactory($q, UserService, TeamService) {
|
||||
function AccessServiceFactory($q, $async, UserService, TeamService) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
|
||||
|
@ -15,6 +16,7 @@ angular.module('portainer.app').factory('AccessService', [
|
|||
const role = _.find(roles, (role) => role.Id === roleId);
|
||||
return role ? role : { Id: 0, Name: '-' };
|
||||
}
|
||||
return { Id: 0, Name: '-' };
|
||||
}
|
||||
|
||||
function _mapAccessData(accesses, authorizedPolicies, inheritedPolicies, roles) {
|
||||
|
@ -50,7 +52,7 @@ angular.module('portainer.app').factory('AccessService', [
|
|||
};
|
||||
}
|
||||
|
||||
service.accesses = function (authorizedUserPolicies, authorizedTeamPolicies, inheritedUserPolicies, inheritedTeamPolicies, roles) {
|
||||
function getAccesses(authorizedUserPolicies, authorizedTeamPolicies, inheritedUserPolicies, inheritedTeamPolicies, roles) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
$q.all({
|
||||
|
@ -80,7 +82,36 @@ angular.module('portainer.app').factory('AccessService', [
|
|||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
}
|
||||
|
||||
async function accessesAsync(entity, parent, roles) {
|
||||
try {
|
||||
if (!entity) {
|
||||
throw { msg: 'Unable to retrieve accesses' };
|
||||
}
|
||||
if (!entity.UserAccessPolicies) {
|
||||
entity.UserAccessPolicies = {};
|
||||
}
|
||||
if (!entity.TeamAccessPolicies) {
|
||||
entity.TeamAccessPolicies = {};
|
||||
}
|
||||
if (parent && !parent.UserAccessPolicies) {
|
||||
parent.UserAccessPolicies = {};
|
||||
}
|
||||
if (parent && !parent.TeamAccessPolicies) {
|
||||
parent.TeamAccessPolicies = {};
|
||||
}
|
||||
return await getAccesses(entity.UserAccessPolicies, entity.TeamAccessPolicies, parent ? parent.UserAccessPolicies : {}, parent ? parent.TeamAccessPolicies : {}, roles);
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function accesses(entity, parent, roles) {
|
||||
return $async(accessesAsync, entity, parent, roles);
|
||||
}
|
||||
|
||||
service.accesses = accesses;
|
||||
|
||||
service.generateAccessPolicies = function (userAccessPolicies, teamAccessPolicies, selectedUserAccesses, selectedTeamAccesses, selectedRoleId) {
|
||||
const newUserPolicies = _.clone(userAccessPolicies);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { PortainerEndpointTypes } from 'Portainer/models/endpoint/models';
|
||||
|
||||
angular.module('portainer.app').factory('EndpointService', [
|
||||
'$q',
|
||||
'Endpoints',
|
||||
|
@ -57,7 +59,7 @@ angular.module('portainer.app').factory('EndpointService', [
|
|||
service.createLocalEndpoint = function () {
|
||||
var deferred = $q.defer();
|
||||
|
||||
FileUploadService.createEndpoint('local', 1, '', '', 1, [], false)
|
||||
FileUploadService.createEndpoint('local', PortainerEndpointTypes.DockerEnvironment, '', '', 1, [], false)
|
||||
.then(function success(response) {
|
||||
deferred.resolve(response.data);
|
||||
})
|
||||
|
@ -86,7 +88,11 @@ angular.module('portainer.app').factory('EndpointService', [
|
|||
var deferred = $q.defer();
|
||||
|
||||
var endpointURL = URL;
|
||||
if (type !== 4) {
|
||||
if (
|
||||
type !== PortainerEndpointTypes.EdgeAgentOnDockerEnvironment &&
|
||||
type !== PortainerEndpointTypes.AgentOnKubernetesEnvironment &&
|
||||
type !== PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment
|
||||
) {
|
||||
endpointURL = 'tcp://' + URL;
|
||||
}
|
||||
|
||||
|
@ -115,6 +121,20 @@ angular.module('portainer.app').factory('EndpointService', [
|
|||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.createLocalKubernetesEndpoint = function () {
|
||||
var deferred = $q.defer();
|
||||
|
||||
FileUploadService.createEndpoint('local', 5, '', '', 1, [], true, true, true)
|
||||
.then(function success(response) {
|
||||
deferred.resolve(response.data);
|
||||
})
|
||||
.catch(function error(err) {
|
||||
deferred.reject({ msg: 'Unable to create endpoint', err: err });
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.createAzureEndpoint = function (name, applicationId, tenantId, authenticationKey, groupId, tagIds) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import _ from 'lodash-es';
|
||||
import { PorImageRegistryModel } from 'Docker/models/porImageRegistry';
|
||||
import { RegistryTypes } from 'Extensions/registry-management/models/registryTypes';
|
||||
import { RegistryViewModel, RegistryCreateRequest } from '../../models/registry';
|
||||
import { RegistryCreateRequest, RegistryViewModel } from '../../models/registry';
|
||||
|
||||
angular.module('portainer.app').factory('RegistryService', [
|
||||
'$q',
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import _ from 'lodash-es';
|
||||
import { StackViewModel, ExternalStackViewModel } from '../../models/stack';
|
||||
import { ExternalStackViewModel, StackViewModel } from '../../models/stack';
|
||||
|
||||
angular.module('portainer.app').factory('StackService', [
|
||||
'$q',
|
||||
'$async',
|
||||
'Stack',
|
||||
'ResourceControlService',
|
||||
'FileUploadService',
|
||||
'StackHelper',
|
||||
'ServiceService',
|
||||
'ContainerService',
|
||||
'SwarmService',
|
||||
'EndpointProvider',
|
||||
function StackServiceFactory($q, Stack, ResourceControlService, FileUploadService, StackHelper, ServiceService, ContainerService, SwarmService, EndpointProvider) {
|
||||
function StackServiceFactory($q, $async, Stack, FileUploadService, StackHelper, ServiceService, ContainerService, SwarmService, EndpointProvider) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
|
||||
|
@ -252,7 +252,6 @@ angular.module('portainer.app').factory('StackService', [
|
|||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.createComposeStackFromFileContent = function (name, stackFileContent, env, endpointId) {
|
||||
var payload = {
|
||||
Name: name,
|
||||
|
@ -333,6 +332,23 @@ angular.module('portainer.app').factory('StackService', [
|
|||
return action(name, stackFileContent, env, endpointId);
|
||||
};
|
||||
|
||||
async function kubernetesDeployAsync(endpointId, namespace, content, compose) {
|
||||
try {
|
||||
const payload = {
|
||||
StackFileContent: content,
|
||||
ComposeFormat: compose,
|
||||
Namespace: namespace,
|
||||
};
|
||||
await Stack.create({ method: 'undefined', type: 3, endpointId: endpointId }, payload).$promise;
|
||||
} catch (err) {
|
||||
throw { err: err };
|
||||
}
|
||||
}
|
||||
|
||||
service.kubernetesDeploy = function (endpointId, namespace, content, compose) {
|
||||
return $async(kubernetesDeployAsync, endpointId, namespace, content, compose);
|
||||
};
|
||||
|
||||
return service;
|
||||
},
|
||||
]);
|
||||
|
|
|
@ -2,8 +2,9 @@ import { TagViewModel } from '../../models/tag';
|
|||
|
||||
angular.module('portainer.app').factory('TagService', [
|
||||
'$q',
|
||||
'$async',
|
||||
'Tags',
|
||||
function TagServiceFactory($q, Tags) {
|
||||
function TagServiceFactory($q, $async, Tags) {
|
||||
'use strict';
|
||||
var service = {};
|
||||
|
||||
|
@ -37,7 +38,7 @@ angular.module('portainer.app').factory('TagService', [
|
|||
return deferred.promise;
|
||||
};
|
||||
|
||||
service.createTag = async function (name) {
|
||||
async function createTagAsync(name) {
|
||||
var payload = {
|
||||
Name: name,
|
||||
};
|
||||
|
@ -47,7 +48,12 @@ angular.module('portainer.app').factory('TagService', [
|
|||
} catch (err) {
|
||||
throw { msg: 'Unable to create tag', err };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function createTag(name) {
|
||||
return $async(createTagAsync, name);
|
||||
}
|
||||
service.createTag = createTag;
|
||||
|
||||
service.deleteTag = function (id) {
|
||||
return Tags.remove({ id: id }).$promise;
|
||||
|
|
|
@ -34,13 +34,21 @@ angular.module('portainer.app').factory('Authentication', [
|
|||
}
|
||||
}
|
||||
|
||||
function logout() {
|
||||
async function logoutAsync(performApiLogout) {
|
||||
if (performApiLogout) {
|
||||
await Auth.logout().$promise;
|
||||
}
|
||||
|
||||
StateManager.clean();
|
||||
EndpointProvider.clean();
|
||||
LocalStorage.clean();
|
||||
LocalStorage.storeLoginStateUUID('');
|
||||
}
|
||||
|
||||
function logout(performApiLogout) {
|
||||
return $async(logoutAsync, performApiLogout);
|
||||
}
|
||||
|
||||
function init() {
|
||||
return $async(initAsync);
|
||||
}
|
||||
|
|
|
@ -126,6 +126,13 @@ angular.module('portainer.app').factory('LocalStorage', [
|
|||
getJobImage: function () {
|
||||
return localStorageService.get('job_image');
|
||||
},
|
||||
storeActiveTab: function (key, index) {
|
||||
return localStorageService.set('active_tab_' + key, index);
|
||||
},
|
||||
getActiveTab: function (key) {
|
||||
const activeTab = localStorageService.get('active_tab_' + key);
|
||||
return activeTab === null ? 0 : activeTab;
|
||||
},
|
||||
storeLogoutReason: (reason) => localStorageService.set('logout_reason', reason),
|
||||
getLogoutReason: () => localStorageService.get('logout_reason'),
|
||||
cleanLogoutReason: () => localStorageService.remove('logout_reason'),
|
||||
|
|
|
@ -128,6 +128,21 @@ angular.module('portainer.app').factory('ModalService', [
|
|||
});
|
||||
};
|
||||
|
||||
service.confirmUpdate = function (message, callback) {
|
||||
message = $sanitize(message);
|
||||
service.confirm({
|
||||
title: 'Are you sure ?',
|
||||
message: message,
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: 'Update',
|
||||
className: 'btn-warning',
|
||||
},
|
||||
},
|
||||
callback: callback,
|
||||
});
|
||||
};
|
||||
|
||||
service.confirmContainerDeletion = function (title, callback) {
|
||||
title = $sanitize(title);
|
||||
prompt({
|
||||
|
|
|
@ -16,7 +16,9 @@ angular.module('portainer.app').factory('Notifications', [
|
|||
|
||||
service.error = function (title, e, fallbackText) {
|
||||
var msg = fallbackText;
|
||||
if (e.err && e.err.data && e.err.data.details) {
|
||||
if (e.err && e.err.data && e.err.data.message) {
|
||||
msg = e.err.data.message;
|
||||
} else if (e.err && e.err.data && e.err.data.details) {
|
||||
msg = e.err.data.details;
|
||||
} else if (e.data && e.data.details) {
|
||||
msg = e.data.details;
|
||||
|
@ -26,8 +28,6 @@ angular.module('portainer.app').factory('Notifications', [
|
|||
msg = e.data.content;
|
||||
} else if (e.message) {
|
||||
msg = e.message;
|
||||
} else if (e.err && e.err.data && e.err.data.message) {
|
||||
msg = e.err.data.message;
|
||||
} else if (e.err && e.err.data && e.err.data.length > 0 && e.err.data[0].message) {
|
||||
msg = e.err.data[0].message;
|
||||
} else if (e.err && e.err.data && e.err.data.err) {
|
||||
|
|
|
@ -173,6 +173,12 @@ angular.module('portainer.app').factory('StateManager', [
|
|||
LocalStorage.storeEndpointState(state.endpoint);
|
||||
deferred.resolve();
|
||||
return deferred.promise;
|
||||
} else if (endpoint.Type === 5 || endpoint.Type === 6 || endpoint.Type === 7) {
|
||||
state.endpoint.name = endpoint.Name;
|
||||
state.endpoint.mode = { provider: 'KUBERNETES' };
|
||||
LocalStorage.storeEndpointState(state.endpoint);
|
||||
deferred.resolve();
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
$q.all({
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { PortainerEndpointTypes } from 'Portainer/models/endpoint/models';
|
||||
import { EndpointSecurityFormData } from '../../../components/endpointSecurity/porEndpointSecurityModel';
|
||||
|
||||
angular
|
||||
|
@ -19,6 +20,7 @@ angular
|
|||
$scope.state = {
|
||||
EnvironmentType: 'agent',
|
||||
actionInProgress: false,
|
||||
deploymentTab: 0,
|
||||
allowCreateTag: Authentication.isAdmin(),
|
||||
availableEdgeAgentCheckinOptions: [
|
||||
{ key: 'Use default interval', value: 0 },
|
||||
|
@ -54,9 +56,12 @@ angular
|
|||
};
|
||||
|
||||
$scope.copyAgentCommand = function () {
|
||||
clipboard.copyText('curl -L https://downloads.portainer.io/agent-stack.yml -o agent-stack.yml && docker stack deploy --compose-file=agent-stack.yml portainer-agent');
|
||||
$('#copyNotification').show();
|
||||
$('#copyNotification').fadeOut(2000);
|
||||
if ($scope.state.deploymentTab === 0) {
|
||||
clipboard.copyText('curl -L https://downloads.portainer.io/agent-stack.yml -o agent-stack.yml && docker stack deploy --compose-file=agent-stack.yml portainer-agent');
|
||||
} else {
|
||||
clipboard.copyText('curl -L https://downloads.portainer.io/portainer-agent-k8s.yaml -o portainer-agent-k8s.yaml; kubectl apply -f portainer-agent-k8s.yaml');
|
||||
}
|
||||
$('#copyNotification').show().fadeOut(2500);
|
||||
};
|
||||
|
||||
$scope.setDefaultPortainerInstanceURL = function () {
|
||||
|
@ -67,6 +72,20 @@ angular
|
|||
$scope.formValues.URL = '';
|
||||
};
|
||||
|
||||
$scope.onCreateTag = function onCreateTag(tagName) {
|
||||
return $async(onCreateTagAsync, tagName);
|
||||
};
|
||||
|
||||
async function onCreateTagAsync(tagName) {
|
||||
try {
|
||||
const tag = await TagService.createTag(tagName);
|
||||
$scope.availableTags = $scope.availableTags.concat(tag);
|
||||
$scope.formValues.TagIds = $scope.formValues.TagIds.concat(tag.Id);
|
||||
} catch (err) {
|
||||
Notifications.error('Failue', err, 'Unable to create tag');
|
||||
}
|
||||
}
|
||||
|
||||
$scope.addDockerEndpoint = function () {
|
||||
var name = $scope.formValues.Name;
|
||||
var URL = $filter('stripprotocol')($scope.formValues.URL);
|
||||
|
@ -83,7 +102,7 @@ angular
|
|||
var TLSCertFile = TLSSkipClientVerify ? null : securityData.TLSCert;
|
||||
var TLSKeyFile = TLSSkipClientVerify ? null : securityData.TLSKey;
|
||||
|
||||
addEndpoint(name, 1, URL, publicURL, groupId, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile);
|
||||
addEndpoint(name, PortainerEndpointTypes.DockerEnvironment, URL, publicURL, groupId, tagIds, TLS, TLSSkipVerify, TLSSkipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile);
|
||||
};
|
||||
|
||||
$scope.addAgentEndpoint = function () {
|
||||
|
@ -93,7 +112,9 @@ angular
|
|||
var groupId = $scope.formValues.GroupId;
|
||||
var tagIds = $scope.formValues.TagIds;
|
||||
|
||||
addEndpoint(name, 2, URL, publicURL, groupId, tagIds, true, true, true, null, null, null);
|
||||
addEndpoint(name, PortainerEndpointTypes.AgentOnDockerEnvironment, URL, publicURL, groupId, tagIds, true, true, true, null, null, null);
|
||||
// TODO: k8s merge - temporarily updated to AgentOnKubernetesEnvironment, breaking Docker agent support
|
||||
// addEndpoint(name, PortainerEndpointTypes.AgentOnKubernetesEnvironment, URL, publicURL, groupId, tags, true, true, true, null, null, null);
|
||||
};
|
||||
|
||||
$scope.addEdgeAgentEndpoint = function () {
|
||||
|
@ -102,7 +123,9 @@ angular
|
|||
var tagIds = $scope.formValues.TagIds;
|
||||
var URL = $scope.formValues.URL;
|
||||
|
||||
addEndpoint(name, 4, URL, '', groupId, tagIds, false, false, false, null, null, null, $scope.formValues.CheckinInterval);
|
||||
addEndpoint(name, PortainerEndpointTypes.EdgeAgentOnDockerEnvironment, URL, '', groupId, tagIds, false, false, false, null, null, null, $scope.formValues.CheckinInterval);
|
||||
// TODO: k8s merge - temporarily updated to EdgeAgentOnKubernetesEnvironment, breaking Docker Edge agent support
|
||||
// addEndpoint(name, PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment, URL, "", groupId, tags, false, false, false, null, null, null);
|
||||
};
|
||||
|
||||
$scope.addAzureEndpoint = function () {
|
||||
|
@ -116,20 +139,6 @@ angular
|
|||
createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tagIds);
|
||||
};
|
||||
|
||||
$scope.onCreateTag = function onCreateTag(tagName) {
|
||||
return $async(onCreateTagAsync, tagName);
|
||||
};
|
||||
|
||||
async function onCreateTagAsync(tagName) {
|
||||
try {
|
||||
const tag = await TagService.createTag(tagName);
|
||||
$scope.availableTags = $scope.availableTags.concat(tag);
|
||||
$scope.formValues.TagIds = $scope.formValues.TagIds.concat(tag.Id);
|
||||
} catch (err) {
|
||||
Notifications.error('Failue', err, 'Unable to create tag');
|
||||
}
|
||||
}
|
||||
|
||||
function createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tagIds) {
|
||||
$scope.state.actionInProgress = true;
|
||||
EndpointService.createAzureEndpoint(name, applicationId, tenantId, authenticationKey, groupId, tagIds)
|
||||
|
@ -164,8 +173,10 @@ angular
|
|||
)
|
||||
.then(function success(data) {
|
||||
Notifications.success('Endpoint created', name);
|
||||
if (type === 4) {
|
||||
if (type === PortainerEndpointTypes.EdgeAgentOnDockerEnvironment || type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) {
|
||||
$state.go('portainer.endpoints.endpoint', { id: data.Id });
|
||||
} else if (type === PortainerEndpointTypes.AgentOnKubernetesEnvironment) {
|
||||
$state.go('portainer.endpoints.endpoint.kubernetesConfig', { id: data.Id });
|
||||
} else {
|
||||
$state.go('portainer.endpoints', {}, { reload: true });
|
||||
}
|
||||
|
|
|
@ -75,13 +75,25 @@
|
|||
<span class="col-sm-12 text-muted small">
|
||||
Ensure that you have deployed the Portainer agent in your cluster first. You can use execute the following command on any manager node to deploy it.
|
||||
<div style="margin-top: 10px;">
|
||||
<code>
|
||||
curl -L https://downloads.portainer.io/agent-stack.yml -o agent-stack.yml && docker stack deploy --compose-file=agent-stack.yml portainer-agent
|
||||
</code>
|
||||
<span class="btn btn-primary btn-sm space-left" ng-click="copyAgentCommand()"><i class="fa fa-copy space-right" aria-hidden="true"></i>Copy</span>
|
||||
<span>
|
||||
<i id="copyNotification" class="fa fa-check green-icon" aria-hidden="true" style="margin-left: 7px; display: none;"></i>
|
||||
</span>
|
||||
<uib-tabset active="state.deploymentTab">
|
||||
<uib-tab index="0" heading="Kubernetes">
|
||||
<code style="display: block; white-space: pre-wrap;">
|
||||
curl -L https://downloads.portainer.io/portainer-agent-k8s.yaml -o portainer-agent-k8s.yaml; kubectl apply -f portainer-agent-k8s.yaml
|
||||
</code>
|
||||
</uib-tab>
|
||||
|
||||
<uib-tab index="0" heading="Docker">
|
||||
<code>
|
||||
curl -L https://downloads.portainer.io/agent-stack.yml -o agent-stack.yml && docker stack deploy --compose-file=agent-stack.yml portainer-agent
|
||||
</code>
|
||||
</uib-tab>
|
||||
</uib-tabset>
|
||||
<div style="margin-top: 10px;">
|
||||
<span class="btn btn-primary btn-sm space-left" ng-click="copyAgentCommand()"><i class="fa fa-copy space-right" aria-hidden="true"></i>Copy command</span>
|
||||
<span>
|
||||
<i id="copyNotification" class="fa fa-check green-icon" aria-hidden="true" style="margin-left: 7px; display: none;"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -92,8 +104,11 @@
|
|||
</div>
|
||||
<div class="form-group">
|
||||
<span class="col-sm-12 text-muted small">
|
||||
Allows you to create an endpoint that can be registered with an Edge agent. The Edge agent will initiate the communications with the Portainer instance. All the
|
||||
required information on how to connect an Edge agent to this endpoint will be available after endpoint creation.
|
||||
<p>
|
||||
Allows you to create an endpoint that can be registered with an Edge agent. The Edge agent will initiate the communications with the Portainer instance. All the
|
||||
required information on how to connect an Edge agent to this endpoint will be available after endpoint creation.
|
||||
</p>
|
||||
<p> You can read more about the Edge agent in the userguide available <a href="https://downloads.portainer.io/edge_agent_guide.pdf">here.</a> </p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -127,7 +142,15 @@
|
|||
<div class="form-group">
|
||||
<label for="container_name" class="col-sm-3 col-lg-2 control-label text-left">Name</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" name="container_name" ng-model="formValues.Name" placeholder="e.g. docker-prod01" required auto-focus />
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="container_name"
|
||||
ng-model="formValues.Name"
|
||||
placeholder="e.g. docker-prod01 / kubernetes-cluster01"
|
||||
required
|
||||
auto-focus
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="endpointCreationForm.container_name.$invalid">
|
||||
|
@ -146,7 +169,8 @@
|
|||
<portainer-tooltip
|
||||
position="bottom"
|
||||
message="URL or IP address of a Docker host. The Docker API must be exposed over a TCP port. Please refer to the Docker documentation to configure it."
|
||||
></portainer-tooltip>
|
||||
>
|
||||
</portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input
|
||||
|
@ -178,8 +202,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- !endpoint-url-input -->
|
||||
<!-- portainer-instance-input -->
|
||||
<div ng-if="state.EnvironmentType === 'edge_agent'">
|
||||
<!-- portainer-instance-input -->
|
||||
<div class="form-group">
|
||||
<label for="endpoint_url" class="col-sm-3 col-lg-2 control-label text-left">
|
||||
Portainer server URL
|
||||
|
@ -203,7 +227,8 @@
|
|||
<portainer-tooltip
|
||||
position="bottom"
|
||||
message="Interval used by this Edge agent to check in with the Portainer instance. Affects Edge endpoint management and Edge compute features."
|
||||
></portainer-tooltip>
|
||||
>
|
||||
</portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-10">
|
||||
<select
|
||||
|
@ -223,7 +248,8 @@
|
|||
<portainer-tooltip
|
||||
position="bottom"
|
||||
message="URL or IP address where exposed containers will be reachable. This field is optional and will default to the endpoint URL."
|
||||
></portainer-tooltip>
|
||||
>
|
||||
</portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" id="endpoint_public_url" ng-model="formValues.PublicURL" placeholder="e.g. 10.0.0.10 or mydocker.mydomain.com" />
|
||||
|
@ -319,13 +345,8 @@
|
|||
<!-- !group -->
|
||||
<!-- tags -->
|
||||
<div class="form-group">
|
||||
<tag-selector
|
||||
ng-if="formValues && availableTags"
|
||||
tags="availableTags"
|
||||
model="formValues.TagIds"
|
||||
allow-create="state.allowCreateTag"
|
||||
on-create="(onCreateTag)"
|
||||
></tag-selector>
|
||||
<tag-selector ng-if="formValues && availableTags" tags="availableTags" model="formValues.TagIds" allow-create="state.allowCreateTag" on-create="(onCreateTag)">
|
||||
</tag-selector>
|
||||
</div>
|
||||
<!-- !tags -->
|
||||
<div class="col-sm-12 form-section-title">
|
||||
|
|
|
@ -10,11 +10,11 @@
|
|||
</rd-header>
|
||||
|
||||
<div class="row">
|
||||
<information-panel ng-if="endpoint.Type === 4 && endpoint.EdgeID" title-text="Edge information">
|
||||
<information-panel ng-if="state.edgeEndpoint && endpoint.EdgeID" title-text="Edge information">
|
||||
<span class="small text-muted">
|
||||
<p>
|
||||
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
This Edge endpoint is associated to an Edge environment.
|
||||
This Edge endpoint is associated to an Edge environment {{ state.kubernetesEndpoint ? '(Kubernetes)' : '(Docker)' }}.
|
||||
</p>
|
||||
<p>
|
||||
Edge key: <code>{{ endpoint.EdgeKey }}</code>
|
||||
|
@ -24,11 +24,11 @@
|
|||
</p>
|
||||
</span>
|
||||
</information-panel>
|
||||
<information-panel ng-if="endpoint.Type === 4 && !endpoint.EdgeID" title-text="Deploy an agent">
|
||||
<information-panel ng-if="state.edgeEndpoint && !endpoint.EdgeID" title-text="Deploy an agent">
|
||||
<span class="small text-muted">
|
||||
<p>
|
||||
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Deploy the Edge agent on your remote Docker environment using the following command(s)
|
||||
Deploy the Edge agent on your remote Docker/Kubernetes environment using the following command(s)
|
||||
</p>
|
||||
<p>
|
||||
The agent will communicate with Portainer via <u>{{ edgeKeyDetails.instanceURL }}</u> and <u>tcp://{{ edgeKeyDetails.tunnelServerAddr }}</u>
|
||||
|
@ -41,6 +41,11 @@
|
|||
<uib-tab index="1" heading="Swarm">
|
||||
<code style="display: block; white-space: pre-wrap; padding: 16px 90px;">{{ dockerCommands.swarm }}</code>
|
||||
</uib-tab>
|
||||
<uib-tab index="0" heading="Kubernetes">
|
||||
<code style="display: block; white-space: pre-wrap;">
|
||||
curl https://downloads.portainer.io/portainer-edge-agent-setup.sh | sudo bash -s -- {{ randomEdgeID }} {{ endpoint.EdgeKey }}
|
||||
</code>
|
||||
</uib-tab>
|
||||
</uib-tabset>
|
||||
<div style="margin-top: 10px;">
|
||||
<span class="btn btn-primary btn-sm" ng-click="copyEdgeAgentDeploymentCommand()"><i class="fa fa-copy space-right" aria-hidden="true"></i>Copy command</span>
|
||||
|
@ -66,6 +71,13 @@
|
|||
</div>
|
||||
</span>
|
||||
</information-panel>
|
||||
<information-panel ng-if="state.kubernetesEndpoint && (!state.edgeEndpoint || (state.edgeEndpoint && endpoint.EdgeID))" title-text="Kubernetes features configuration">
|
||||
<span class="small text-muted">
|
||||
<i class="fa fa-tools blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
You should configure the features available in this Kubernetes environment in the
|
||||
<a ui-sref="portainer.endpoints.endpoint.kubernetesConfig({id: endpoint.Id})">Kubernetes configuration</a> view.
|
||||
</span>
|
||||
</information-panel>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
|
@ -80,22 +92,23 @@
|
|||
<div class="form-group">
|
||||
<label for="container_name" class="col-sm-3 col-lg-2 control-label text-left">Name</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" id="container_name" ng-model="endpoint.Name" placeholder="e.g. docker-prod01" />
|
||||
<input type="text" class="form-control" id="container_name" ng-model="endpoint.Name" placeholder="e.g. kubernetes-cluster01 / docker-prod01" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<!-- endpoint-url-input -->
|
||||
<div class="form-group" ng-if="endpoint.Type !== 4">
|
||||
<div class="form-group" ng-if="!state.edgeEndpoint">
|
||||
<label for="endpoint_url" class="col-sm-3 col-lg-2 control-label text-left">
|
||||
Endpoint URL
|
||||
<portainer-tooltip
|
||||
position="bottom"
|
||||
message="URL or IP address of a Docker host. The Docker API must be exposed over a TCP port. Please refer to the Docker documentation to configure it."
|
||||
></portainer-tooltip>
|
||||
>
|
||||
</portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input
|
||||
ng-disabled="endpointType === 'local' || endpoint.Type === 3"
|
||||
ng-disabled="endpointType === 'local' || state.azureEndpoint"
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="endpoint_url"
|
||||
|
@ -106,19 +119,20 @@
|
|||
</div>
|
||||
<!-- !endpoint-url-input -->
|
||||
<!-- endpoint-public-url-input -->
|
||||
<div class="form-group" ng-if="endpoint.Type !== 3">
|
||||
<div class="form-group" ng-if="!state.azureEndpoint">
|
||||
<label for="endpoint_public_url" class="col-sm-3 col-lg-2 control-label text-left">
|
||||
Public IP
|
||||
<portainer-tooltip
|
||||
position="bottom"
|
||||
message="URL or IP address where exposed containers will be reachable. This field is optional and will default to the endpoint URL."
|
||||
></portainer-tooltip>
|
||||
>
|
||||
</portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-9 col-lg-10">
|
||||
<input type="text" class="form-control" id="endpoint_public_url" ng-model="endpoint.PublicURL" placeholder="e.g. 10.0.0.10 or mydocker.mydomain.com" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-if="endpoint.Type === 4">
|
||||
<div class="form-group" ng-if="state.edgeEndpoint">
|
||||
<label for="edge_checkin" class="col-sm-2 control-label text-left">
|
||||
Poll frequency
|
||||
<portainer-tooltip
|
||||
|
@ -135,13 +149,13 @@
|
|||
></select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !endpoint-public-url-input -->
|
||||
<azure-endpoint-config
|
||||
ng-if="endpoint.Type === 3"
|
||||
ng-if="state.azureEndpoint"
|
||||
application-id="endpoint.AzureCredentials.ApplicationID"
|
||||
tenant-id="endpoint.AzureCredentials.TenantID"
|
||||
authentication-key="endpoint.AzureCredentials.AuthenticationKey"
|
||||
></azure-endpoint-config>
|
||||
<!-- !endpoint-public-url-input -->
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Metadata
|
||||
</div>
|
||||
|
@ -161,7 +175,7 @@
|
|||
</div>
|
||||
<!-- !tags -->
|
||||
<!-- endpoint-security -->
|
||||
<div ng-if="endpointType === 'remote' && endpoint.Type !== 3 && endpoint.Type !== 4">
|
||||
<div ng-if="endpointType === 'remote' && !state.azureEndpoint && !state.edgeEndpoint && endpoint.Type !== 6">
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Security
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import _ from 'lodash-es';
|
||||
import uuidv4 from 'uuid/v4';
|
||||
import { PortainerEndpointTypes } from 'Portainer/models/endpoint/models';
|
||||
import { EndpointSecurityFormData } from '../../../components/endpointSecurity/porEndpointSecurityModel';
|
||||
|
||||
angular
|
||||
|
@ -24,6 +25,10 @@ angular
|
|||
uploadInProgress: false,
|
||||
actionInProgress: false,
|
||||
deploymentTab: 0,
|
||||
azureEndpoint: false,
|
||||
kubernetesEndpoint: false,
|
||||
agentEndpoint: false,
|
||||
edgeEndpoint: false,
|
||||
allowCreate: Authentication.isAdmin(),
|
||||
availableEdgeAgentCheckinOptions: [
|
||||
{ key: 'Use default interval', value: 0 },
|
||||
|
@ -58,7 +63,7 @@ angular
|
|||
$scope.endpoint.EdgeKey +
|
||||
' -e CAP_HOST_MANAGEMENT=1 -v portainer_agent_data:/data --name portainer_edge_agent portainer/agent'
|
||||
);
|
||||
} else {
|
||||
} else if ($scope.state.deploymentTab === 1) {
|
||||
clipboard.copyText(
|
||||
'docker network create --driver overlay portainer_agent_network; docker service create --name portainer_edge_agent --network portainer_agent_network -e AGENT_CLUSTER_ADDR=tasks.portainer_edge_agent -e EDGE=1 -e EDGE_ID=' +
|
||||
$scope.randomEdgeID +
|
||||
|
@ -66,6 +71,8 @@ angular
|
|||
$scope.endpoint.EdgeKey +
|
||||
" -e CAP_HOST_MANAGEMENT=1 --mode global --constraint 'node.platform.os == linux' --mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock --mount type=bind,src=//var/lib/docker/volumes,dst=/var/lib/docker/volumes --mount type=bind,src=//,dst=/host --mount type=volume,src=portainer_agent_data,dst=/data portainer/agent"
|
||||
);
|
||||
} else {
|
||||
clipboard.copyText('curl https://downloads.portainer.io/portainer-edge-agent-setup.sh | bash -s -- ' + $scope.randomEdgeID + ' ' + $scope.endpoint.EdgeKey);
|
||||
}
|
||||
$('#copyNotificationDeploymentCommand').show().fadeOut(2500);
|
||||
};
|
||||
|
@ -114,7 +121,12 @@ angular
|
|||
AzureAuthenticationKey: endpoint.AzureCredentials.AuthenticationKey,
|
||||
};
|
||||
|
||||
if ($scope.endpointType !== 'local' && endpoint.Type !== 3) {
|
||||
if (
|
||||
$scope.endpointType !== 'local' &&
|
||||
endpoint.Type !== PortainerEndpointTypes.AzureEnvironment &&
|
||||
endpoint.Type !== PortainerEndpointTypes.KubernetesLocalEnvironment &&
|
||||
endpoint.Type !== PortainerEndpointTypes.AgentOnKubernetesEnvironment
|
||||
) {
|
||||
payload.URL = 'tcp://' + endpoint.URL;
|
||||
}
|
||||
|
||||
|
@ -151,6 +163,30 @@ angular
|
|||
return keyInformation;
|
||||
}
|
||||
|
||||
function configureState() {
|
||||
if (
|
||||
$scope.endpoint.Type === PortainerEndpointTypes.KubernetesLocalEnvironment ||
|
||||
$scope.endpoint.Type === PortainerEndpointTypes.AgentOnKubernetesEnvironment ||
|
||||
$scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment
|
||||
) {
|
||||
$scope.state.kubernetesEndpoint = true;
|
||||
}
|
||||
if ($scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnDockerEnvironment || $scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) {
|
||||
$scope.state.edgeEndpoint = true;
|
||||
}
|
||||
if ($scope.endpoint.Type === PortainerEndpointTypes.AzureEnvironment) {
|
||||
$scope.state.azureEndpoint = true;
|
||||
}
|
||||
if (
|
||||
$scope.endpoint.Type === PortainerEndpointTypes.AgentOnDockerEnvironment ||
|
||||
$scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnDockerEnvironment ||
|
||||
$scope.endpoint.Type === PortainerEndpointTypes.AgentOnKubernetesEnvironment ||
|
||||
$scope.endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment
|
||||
) {
|
||||
$scope.state.agentEndpoint = true;
|
||||
}
|
||||
}
|
||||
|
||||
function initView() {
|
||||
$q.all({
|
||||
endpoint: EndpointService.endpoint($transition$.params().id),
|
||||
|
@ -166,7 +202,7 @@ angular
|
|||
$scope.endpointType = 'remote';
|
||||
}
|
||||
endpoint.URL = $filter('stripprotocol')(endpoint.URL);
|
||||
if (endpoint.Type === 4) {
|
||||
if (endpoint.Type === PortainerEndpointTypes.EdgeAgentOnDockerEnvironment || endpoint.Type === PortainerEndpointTypes.EdgeAgentOnKubernetesEnvironment) {
|
||||
$scope.edgeKeyDetails = decodeEdgeKey(endpoint.EdgeKey);
|
||||
$scope.randomEdgeID = uuidv4();
|
||||
$scope.dockerCommands = {
|
||||
|
@ -180,6 +216,7 @@ angular
|
|||
$scope.endpoint = endpoint;
|
||||
$scope.groups = data.groups;
|
||||
$scope.availableTags = data.tags;
|
||||
configureState();
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to retrieve endpoint details');
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
<motd-panel ng-if="motd && motd.Message !== '' && applicationState.UI.dismissedInfoHash !== motd.Hash" motd="motd" dismiss-action="dismissImportantInformation(motd.Hash)">
|
||||
</motd-panel>
|
||||
|
||||
<kubernetes-feedback-panel></kubernetes-feedback-panel>
|
||||
|
||||
<information-panel ng-if="!isAdmin && endpoints.length === 0" title-text="Information">
|
||||
<span class="small text-muted">
|
||||
<p>
|
||||
|
|
|
@ -15,7 +15,8 @@ angular
|
|||
LegacyExtensionManager,
|
||||
ModalService,
|
||||
MotdService,
|
||||
SystemService
|
||||
SystemService,
|
||||
KubernetesHealthService
|
||||
) {
|
||||
$scope.state = {
|
||||
connectingToEdgeEndpoint: false,
|
||||
|
@ -30,6 +31,10 @@ angular
|
|||
return switchToAzureEndpoint(endpoint);
|
||||
} else if (endpoint.Type === 4) {
|
||||
return switchToEdgeEndpoint(endpoint);
|
||||
} else if (endpoint.Type === 5 || endpoint.Type === 6) {
|
||||
return switchToKubernetesEndpoint(endpoint);
|
||||
} else if (endpoint.Type === 7) {
|
||||
return switchToKubernetesEdgeEndpoint(endpoint);
|
||||
}
|
||||
|
||||
checkEndpointStatus(endpoint)
|
||||
|
@ -102,6 +107,17 @@ angular
|
|||
});
|
||||
}
|
||||
|
||||
function switchToKubernetesEndpoint(endpoint) {
|
||||
EndpointProvider.setEndpointID(endpoint.Id);
|
||||
StateManager.updateEndpointState(endpoint, [])
|
||||
.then(function success() {
|
||||
$state.go('kubernetes.dashboard');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to connect to the Kubernetes endpoint');
|
||||
});
|
||||
}
|
||||
|
||||
function switchToEdgeEndpoint(endpoint) {
|
||||
if (!endpoint.EdgeID) {
|
||||
$state.go('portainer.endpoints.endpoint', { id: endpoint.Id });
|
||||
|
@ -121,6 +137,26 @@ angular
|
|||
});
|
||||
}
|
||||
|
||||
function switchToKubernetesEdgeEndpoint(endpoint) {
|
||||
if (!endpoint.EdgeID) {
|
||||
$state.go('portainer.endpoints.endpoint', { id: endpoint.Id });
|
||||
return;
|
||||
}
|
||||
|
||||
EndpointProvider.setEndpointID(endpoint.Id);
|
||||
$scope.state.connectingToEdgeEndpoint = true;
|
||||
KubernetesHealthService.ping()
|
||||
.then(function success() {
|
||||
endpoint.Status = 1;
|
||||
})
|
||||
.catch(function error() {
|
||||
endpoint.Status = 2;
|
||||
})
|
||||
.finally(function final() {
|
||||
switchToKubernetesEndpoint(endpoint);
|
||||
});
|
||||
}
|
||||
|
||||
function switchToDockerEndpoint(endpoint) {
|
||||
if (endpoint.Status === 2 && endpoint.Snapshots[0] && endpoint.Snapshots[0].Swarm === true) {
|
||||
$scope.state.connectingToEdgeEndpoint = false;
|
||||
|
|
34
app/portainer/views/init/endpoint/includes/agent.html
Normal file
34
app/portainer/views/init/endpoint/includes/agent.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
<div class="col-sm-12 form-section-title">
|
||||
Information
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<span class="small">
|
||||
<p class="text-primary">
|
||||
Connect directly to a Portainer agent running inside a Docker or Kubernetes environment.
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Environment
|
||||
</div>
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="endpoint_name" class="col-sm-4 col-lg-3 control-label text-left">Name</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input type="text" class="form-control" id="endpoint_name" ng-model="ctrl.formValues.Name" placeholder="e.g. prod-cluster-01" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<!-- endpoint-url-input -->
|
||||
<div class="form-group">
|
||||
<label for="endpoint_url" class="col-sm-4 col-lg-3 control-label text-left">
|
||||
Agent URL
|
||||
<portainer-tooltip position="bottom" message="URL or IP address of a Portainer agent."> </portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input type="text" class="form-control" id="endpoint_url" ng-model="ctrl.formValues.URL" placeholder="e.g. 10.0.0.10:9001" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !endpoint-url-input -->
|
63
app/portainer/views/init/endpoint/includes/azure.html
Normal file
63
app/portainer/views/init/endpoint/includes/azure.html
Normal file
|
@ -0,0 +1,63 @@
|
|||
<div class="col-sm-12 form-section-title">
|
||||
Information
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<span class="small">
|
||||
<p class="text-muted"> <i class="fa fa-flask orange-icon" aria-hidden="true" style="margin-right: 2px;"></i> This feature is experimental. </p>
|
||||
<p class="text-primary">
|
||||
Connect to Microsoft Azure to manage Azure Container Instances (ACI).
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Have a look at
|
||||
<a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal" target="_blank">the Azure documentation</a>
|
||||
to retrieve the credentials required below.
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Environment
|
||||
</div>
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="endpoint_name" class="col-sm-4 col-lg-3 control-label text-left">Name</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input type="text" class="form-control" id="endpoint_name" ng-model="ctrl.formValues.Name" placeholder="e.g. azure-01" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Azure credentials
|
||||
</div>
|
||||
<!-- applicationId-input -->
|
||||
<div class="form-group">
|
||||
<label for="azure_credential_appid" class="col-sm-4 col-lg-3 control-label text-left">Application ID</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input type="text" class="form-control" id="azure_credential_appid" ng-model="ctrl.formValues.AzureApplicationId" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !applicationId-input -->
|
||||
<!-- tenantId-input -->
|
||||
<div class="form-group">
|
||||
<label for="azure_credential_tenantid" class="col-sm-4 col-lg-3 control-label text-left">Tenant ID</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input type="text" class="form-control" id="azure_credential_tenantid" ng-model="ctrl.formValues.AzureTenantId" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !tenantId-input -->
|
||||
<!-- authenticationkey-input -->
|
||||
<div class="form-group">
|
||||
<label for="azure_credential_authkey" class="col-sm-4 col-lg-3 control-label text-left">Authentication key</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="azure_credential_authkey"
|
||||
ng-model="ctrl.formValues.AzureAuthenticationKey"
|
||||
placeholder="cOrXoK/1D35w8YQ8nH1/8ZGwzz45JIYD5jxHKXEQknk="
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !authenticationkey-input -->
|
21
app/portainer/views/init/endpoint/includes/localDocker.html
Normal file
21
app/portainer/views/init/endpoint/includes/localDocker.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
<div class="col-sm-12 form-section-title">
|
||||
Information
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<span class="small">
|
||||
<p class="text-primary">
|
||||
Manage the Docker environment where Portainer is running.
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Ensure that you have started the Portainer container with the following Docker flag:
|
||||
</p>
|
||||
<p class="text-muted"> <code>-v "/var/run/docker.sock:/var/run/docker.sock"</code> (Linux). </p>
|
||||
<p class="text-muted">
|
||||
or
|
||||
</p>
|
||||
<p class="text-muted"> <code>-v \\.\pipe\docker_engine:\\.\pipe\docker_engine</code> (Windows). </p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,12 @@
|
|||
<div class="col-sm-12 form-section-title">
|
||||
Information
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<span class="small">
|
||||
<p class="text-primary">
|
||||
Manage the Kubernetes environment where Portainer is running.
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
120
app/portainer/views/init/endpoint/includes/remote.html
Normal file
120
app/portainer/views/init/endpoint/includes/remote.html
Normal file
|
@ -0,0 +1,120 @@
|
|||
<div class="col-sm-12 form-section-title">
|
||||
Information
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<span class="small">
|
||||
<p class="text-primary">
|
||||
Connect Portainer to a remote Docker environment using the Docker API over TCP.
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
The Docker API must be exposed over TCP. You can find more information about how to expose the Docker API over TCP
|
||||
<a href="https://docs.docker.com/engine/security/https/" target="_blank">in the Docker documentation</a>.
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Environment
|
||||
</div>
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="endpoint_name" class="col-sm-4 col-lg-3 control-label text-left">Name</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input type="text" class="form-control" id="endpoint_name" ng-model="ctrl.formValues.Name" placeholder="e.g. docker-prod01" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<!-- endpoint-url-input -->
|
||||
<div class="form-group">
|
||||
<label for="endpoint_url" class="col-sm-4 col-lg-3 control-label text-left">
|
||||
Endpoint URL
|
||||
<portainer-tooltip position="bottom" message="URL or IP address of a Docker host with API exposed over TCP."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input type="text" class="form-control" id="endpoint_url" ng-model="ctrl.formValues.URL" placeholder="e.g. 10.0.0.10:2375 or mydocker.mydomain.com:2375" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !endpoint-url-input -->
|
||||
<!-- tls-checkbox -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<label for="tls" class="control-label text-left">
|
||||
TLS
|
||||
<portainer-tooltip position="bottom" message="Enable this option if you need to specify TLS certificates to connect to the Docker endpoint."></portainer-tooltip>
|
||||
</label>
|
||||
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" ng-model="ctrl.formValues.TLS" /><i></i> </label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !tls-checkbox -->
|
||||
<!-- tls-options -->
|
||||
<div ng-if="ctrl.formValues.TLS">
|
||||
<!-- skip-server-verification -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-10">
|
||||
<label for="tls_verify" class="control-label text-left">
|
||||
Skip server verification
|
||||
<portainer-tooltip position="bottom" message="Enable this option if you need to authenticate server based on given CA."></portainer-tooltip>
|
||||
</label>
|
||||
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" ng-model="ctrl.formValues.TLSSkipVerify" /><i></i> </label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !skip-server-verification -->
|
||||
<!-- skip-client-verification -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-10">
|
||||
<label for="tls_client_cert" class="control-label text-left">
|
||||
Skip client verification
|
||||
<portainer-tooltip position="bottom" message="Enable this option if you need to authenticate with a client certificate."></portainer-tooltip>
|
||||
</label>
|
||||
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" ng-model="ctrl.formValues.TLSSKipClientVerify" /><i></i> </label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !skip-client-verification -->
|
||||
<div class="col-sm-12 form-section-title" ng-if="!ctrl.formValues.TLSSkipVerify || !ctrl.formValues.TLSSKipClientVerify">
|
||||
Required TLS files
|
||||
</div>
|
||||
<!-- ca-input -->
|
||||
<div class="form-group" ng-if="!ctrl.formValues.TLSSkipVerify">
|
||||
<label class="col-sm-4 col-lg-3 control-label text-left">TLS CA certificate</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<button class="btn btn-sm btn-primary" ngf-select ng-model="ctrl.formValues.TLSCACert">Select file</button>
|
||||
<span style="margin-left: 5px;">
|
||||
{{ ctrl.formValues.TLSCACert.name }}
|
||||
<i class="fa fa-times red-icon" ng-if="!ctrl.formValues.TLSCACert" aria-hidden="true"></i>
|
||||
<i class="fa fa-circle-notch fa-spin" ng-if="ctrl.state.uploadInProgress"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !ca-input -->
|
||||
<div ng-if="!ctrl.formValues.TLSSKipClientVerify">
|
||||
<!-- cert-input -->
|
||||
<div class="form-group">
|
||||
<label for="tls_cert" class="col-sm-4 col-lg-3 control-label text-left">TLS certificate</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<button class="btn btn-sm btn-primary" ngf-select ng-model="ctrl.formValues.TLSCert">Select file</button>
|
||||
<span style="margin-left: 5px;">
|
||||
{{ ctrl.formValues.TLSCert.name }}
|
||||
<i class="fa fa-times red-icon" ng-if="!ctrl.formValues.TLSCert" aria-hidden="true"></i>
|
||||
<i class="fa fa-circle-notch fa-spin" ng-if="ctrl.state.uploadInProgress"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !cert-input -->
|
||||
<!-- key-input -->
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-lg-3 control-label text-left">TLS key</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<button class="btn btn-sm btn-primary" ngf-select ng-model="ctrl.formValues.TLSKey">Select file</button>
|
||||
<span style="margin-left: 5px;">
|
||||
{{ ctrl.formValues.TLSKey.name }}
|
||||
<i class="fa fa-times red-icon" ng-if="!ctrl.formValues.TLSKey" aria-hidden="true"></i>
|
||||
<i class="fa fa-circle-notch fa-spin" ng-if="ctrl.state.uploadInProgress"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !key-input -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- !tls-options -->
|
|
@ -4,8 +4,8 @@
|
|||
<div class="col-sm-12">
|
||||
<!-- simple box logo -->
|
||||
<div class="row">
|
||||
<img ng-if="logo" ng-src="{{ logo }}" class="simple-box-logo" />
|
||||
<img ng-if="!logo" src="~@/assets/images/logo_alt.png" class="simple-box-logo" alt="Portainer" />
|
||||
<img ng-if="ctrl.logo" ng-src="{{ ctrl.logo }}" class="simple-box-logo" />
|
||||
<img ng-if="!ctrl.logo" src="~@/assets/images/logo_alt.png" class="simple-box-logo" alt="Portainer" />
|
||||
</div>
|
||||
<!-- !simple box logo -->
|
||||
<!-- init-endpoint panel -->
|
||||
|
@ -17,7 +17,7 @@
|
|||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<span class="small text-muted">
|
||||
Connect Portainer to the Docker environment you want to manage.
|
||||
Connect Portainer to the container environment you want to manage.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -25,377 +25,52 @@
|
|||
<!-- endpoint-type -->
|
||||
<div class="form-group" style="margin-bottom: 0;">
|
||||
<div class="boxselector_wrapper">
|
||||
<div>
|
||||
<input type="radio" id="local_endpoint" ng-model="formValues.EndpointType" value="local" />
|
||||
<label for="local_endpoint">
|
||||
<div ng-repeat="type in ctrl.endpointSections">
|
||||
<input type="radio" id="{{ type.Id }}" ng-model="ctrl.formValues.ConnectionType" ng-value="type.Value" />
|
||||
<label for="{{ type.Id }}">
|
||||
<div class="boxselector_header">
|
||||
<i class="fab fa-docker" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Local
|
||||
<i ng-class="type.Classes" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
{{ type.Title }}
|
||||
</div>
|
||||
<p>Manage the local Docker environment</p>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="remote_endpoint" ng-model="formValues.EndpointType" value="remote" />
|
||||
<label for="remote_endpoint">
|
||||
<div class="boxselector_header">
|
||||
<i class="fab fa-docker" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Remote
|
||||
</div>
|
||||
<p>Manage a remote Docker environment</p>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="agent_endpoint" ng-model="formValues.EndpointType" value="agent" />
|
||||
<label for="agent_endpoint">
|
||||
<div class="boxselector_header">
|
||||
<i class="fa fa-bolt" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Agent
|
||||
</div>
|
||||
<p>Connect to a Portainer agent</p>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="radio" id="azure_endpoint" ng-model="formValues.EndpointType" value="azure" />
|
||||
<label for="azure_endpoint">
|
||||
<div class="boxselector_header">
|
||||
<i class="fab fa-microsoft" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Azure
|
||||
</div>
|
||||
<p>Connect to Microsoft Azure ACI</p>
|
||||
<p>{{ type.Description }}</p>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !endpoint-type -->
|
||||
<!-- local-endpoint -->
|
||||
<div ng-if="formValues.EndpointType === 'local'">
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Information
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<span class="small">
|
||||
<p class="text-primary">
|
||||
Manage the Docker environment where Portainer is running.
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
<i class="fa fa-exclamation-circle orange-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Ensure that you have started the Portainer container with the following Docker flag:
|
||||
</p>
|
||||
<p class="text-muted"> <code>-v "/var/run/docker.sock:/var/run/docker.sock"</code> (Linux). </p>
|
||||
<p class="text-muted">
|
||||
or
|
||||
</p>
|
||||
<p class="text-muted"> <code>-v \\.\pipe\docker_engine:\\.\pipe\docker_engine</code> (Windows). </p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- actions -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="state.actionInProgress"
|
||||
ng-click="createLocalEndpoint()"
|
||||
button-spinner="state.actionInProgress"
|
||||
>
|
||||
<span ng-hide="state.actionInProgress"><i class="fa fa-bolt" aria-hidden="true"></i> Connect</span>
|
||||
<span ng-show="state.actionInProgress">Connecting...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
<!-- endpoint-type-details -->
|
||||
<div ng-if="ctrl.formValues.ConnectionType === ctrl.PortainerEndpointConnectionTypes.DOCKER_LOCAL">
|
||||
<ng-include src="'app/portainer/views/init/endpoint/includes/localDocker.html'"></ng-include>
|
||||
</div>
|
||||
<!-- !local-endpoint -->
|
||||
<!-- agent-endpoint -->
|
||||
<div ng-if="formValues.EndpointType === 'agent'">
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Information
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<span class="small">
|
||||
<p class="text-primary">
|
||||
Connect directly to a Portainer agent running inside a Swarm cluster.
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
If you have started Portainer in the same overlay network as the agent, you can use <code>tasks.AGENT_SERVICE_NAME:AGENT_SERVICE_PORT</code> as the endpoint
|
||||
URL format.
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Environment
|
||||
</div>
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="endpoint_name" class="col-sm-4 col-lg-3 control-label text-left">Name</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input type="text" class="form-control" id="endpoint_name" ng-model="formValues.Name" placeholder="e.g. docker-prod01" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<!-- endpoint-url-input -->
|
||||
<div class="form-group">
|
||||
<label for="endpoint_url" class="col-sm-4 col-lg-3 control-label text-left">
|
||||
Agent URL
|
||||
<portainer-tooltip position="bottom" message="URL or IP address of a Portainer agent."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input type="text" class="form-control" id="endpoint_url" ng-model="formValues.URL" placeholder="e.g. 10.0.0.10:9001 or tasks.portainer_agent:9001" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !endpoint-url-input -->
|
||||
<!-- actions -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="state.actionInProgress || !formValues.Name || !formValues.URL"
|
||||
ng-click="createAgentEndpoint()"
|
||||
button-spinner="state.actionInProgress"
|
||||
>
|
||||
<span ng-hide="state.actionInProgress"><i class="fa fa-bolt" aria-hidden="true"></i> Connect</span>
|
||||
<span ng-show="state.actionInProgress">Connecting...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
<div ng-if="ctrl.formValues.ConnectionType === ctrl.PortainerEndpointConnectionTypes.KUBERNETES_LOCAL">
|
||||
<ng-include src="'app/portainer/views/init/endpoint/includes/localKubernetes.html'"></ng-include>
|
||||
</div>
|
||||
<!-- !agent-endpoint -->
|
||||
<!-- azure-endpoint -->
|
||||
<div ng-if="formValues.EndpointType === 'azure'">
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Information
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<span class="small">
|
||||
<p class="text-muted"> <i class="fa fa-flask orange-icon" aria-hidden="true" style="margin-right: 2px;"></i> This feature is experimental. </p>
|
||||
<p class="text-primary">
|
||||
Connect to Microsoft Azure to manage Azure Container Instances (ACI).
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
Have a look at
|
||||
<a href="https://docs.microsoft.com/en-us/azure/azure-resource-manager/resource-group-create-service-principal-portal" target="_blank"
|
||||
>the Azure documentation</a
|
||||
>
|
||||
to retrieve the credentials required below.
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Environment
|
||||
</div>
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="endpoint_name" class="col-sm-4 col-lg-3 control-label text-left">Name</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input type="text" class="form-control" id="endpoint_name" ng-model="formValues.Name" placeholder="e.g. azure-01" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Azure credentials
|
||||
</div>
|
||||
<!-- applicationId-input -->
|
||||
<div class="form-group">
|
||||
<label for="azure_credential_appid" class="col-sm-4 col-lg-3 control-label text-left">Application ID</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input type="text" class="form-control" id="azure_credential_appid" ng-model="formValues.AzureApplicationId" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !applicationId-input -->
|
||||
<!-- tenantId-input -->
|
||||
<div class="form-group">
|
||||
<label for="azure_credential_tenantid" class="col-sm-4 col-lg-3 control-label text-left">Tenant ID</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input type="text" class="form-control" id="azure_credential_tenantid" ng-model="formValues.AzureTenantId" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !tenantId-input -->
|
||||
<!-- authenticationkey-input -->
|
||||
<div class="form-group">
|
||||
<label for="azure_credential_authkey" class="col-sm-4 col-lg-3 control-label text-left">Authentication key</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="azure_credential_authkey"
|
||||
ng-model="formValues.AzureAuthenticationKey"
|
||||
placeholder="cOrXoK/1D35w8YQ8nH1/8ZGwzz45JIYD5jxHKXEQknk="
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !authenticationkey-input -->
|
||||
<!-- actions -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="state.actionInProgress || !formValues.Name || !formValues.AzureApplicationId || !formValues.AzureTenantId || !formValues.AzureAuthenticationKey"
|
||||
ng-click="createAzureEndpoint()"
|
||||
button-spinner="state.actionInProgress"
|
||||
>
|
||||
<span ng-hide="state.actionInProgress"><i class="fa fa-bolt" aria-hidden="true"></i> Connect</span>
|
||||
<span ng-show="state.actionInProgress">Connecting...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
<div ng-if="ctrl.formValues.ConnectionType === ctrl.PortainerEndpointConnectionTypes.REMOTE">
|
||||
<ng-include src="'app/portainer/views/init/endpoint/includes/remote.html'"></ng-include>
|
||||
</div>
|
||||
<!-- !azure-endpoint -->
|
||||
<!-- remote-endpoint -->
|
||||
<div ng-if="formValues.EndpointType === 'remote'">
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Information
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<span class="small">
|
||||
<p class="text-primary">
|
||||
Connect Portainer to a remote Docker environment using the Docker API over TCP.
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
|
||||
The Docker API must be exposed over TCP. You can find more information about how to expose the Docker API over TCP
|
||||
<a href="https://docs.docker.com/engine/security/https/" target="_blank">in the Docker documentation</a>.
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Environment
|
||||
</div>
|
||||
<!-- name-input -->
|
||||
<div class="form-group">
|
||||
<label for="endpoint_name" class="col-sm-4 col-lg-3 control-label text-left">Name</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input type="text" class="form-control" id="endpoint_name" ng-model="formValues.Name" placeholder="e.g. docker-prod01" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !name-input -->
|
||||
<!-- endpoint-url-input -->
|
||||
<div class="form-group">
|
||||
<label for="endpoint_url" class="col-sm-4 col-lg-3 control-label text-left">
|
||||
Endpoint URL
|
||||
<portainer-tooltip position="bottom" message="URL or IP address of a Docker host with API exposed over TCP."></portainer-tooltip>
|
||||
</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<input type="text" class="form-control" id="endpoint_url" ng-model="formValues.URL" placeholder="e.g. 10.0.0.10:2375 or mydocker.mydomain.com:2375" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- !endpoint-url-input -->
|
||||
<!-- tls-checkbox -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<label for="tls" class="control-label text-left">
|
||||
TLS
|
||||
<portainer-tooltip
|
||||
position="bottom"
|
||||
message="Enable this option if you need to specify TLS certificates to connect to the Docker endpoint."
|
||||
></portainer-tooltip>
|
||||
</label>
|
||||
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" ng-model="formValues.TLS" /><i></i> </label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !tls-checkbox -->
|
||||
<!-- tls-options -->
|
||||
<div ng-if="formValues.TLS">
|
||||
<!-- skip-server-verification -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-10">
|
||||
<label for="tls_verify" class="control-label text-left">
|
||||
Skip server verification
|
||||
<portainer-tooltip position="bottom" message="Enable this option if you need to authenticate server based on given CA."></portainer-tooltip>
|
||||
</label>
|
||||
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" ng-model="formValues.TLSSkipVerify" /><i></i> </label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !skip-server-verification -->
|
||||
<!-- skip-client-verification -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-10">
|
||||
<label for="tls_client_cert" class="control-label text-left">
|
||||
Skip client verification
|
||||
<portainer-tooltip position="bottom" message="Enable this option if you need to authenticate with a client certificate."></portainer-tooltip>
|
||||
</label>
|
||||
<label class="switch" style="margin-left: 20px;"> <input type="checkbox" ng-model="formValues.TLSSKipClientVerify" /><i></i> </label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !skip-client-verification -->
|
||||
<div class="col-sm-12 form-section-title" ng-if="!formValues.TLSSkipVerify || !formValues.TLSSKipClientVerify">
|
||||
Required TLS files
|
||||
</div>
|
||||
<!-- ca-input -->
|
||||
<div class="form-group" ng-if="!formValues.TLSSkipVerify">
|
||||
<label class="col-sm-4 col-lg-3 control-label text-left">TLS CA certificate</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<button class="btn btn-sm btn-primary" ngf-select ng-model="formValues.TLSCACert">Select file</button>
|
||||
<span style="margin-left: 5px;">
|
||||
{{ formValues.TLSCACert.name }}
|
||||
<i class="fa fa-times red-icon" ng-if="!formValues.TLSCACert" aria-hidden="true"></i>
|
||||
<i class="fa fa-circle-notch fa-spin" ng-if="state.uploadInProgress"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !ca-input -->
|
||||
<div ng-if="!formValues.TLSSKipClientVerify">
|
||||
<!-- cert-input -->
|
||||
<div class="form-group">
|
||||
<label for="tls_cert" class="col-sm-4 col-lg-3 control-label text-left">TLS certificate</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<button class="btn btn-sm btn-primary" ngf-select ng-model="formValues.TLSCert">Select file</button>
|
||||
<span style="margin-left: 5px;">
|
||||
{{ formValues.TLSCert.name }}
|
||||
<i class="fa fa-times red-icon" ng-if="!formValues.TLSCert" aria-hidden="true"></i>
|
||||
<i class="fa fa-circle-notch fa-spin" ng-if="state.uploadInProgress"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !cert-input -->
|
||||
<!-- key-input -->
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 col-lg-3 control-label text-left">TLS key</label>
|
||||
<div class="col-sm-8 col-lg-9">
|
||||
<button class="btn btn-sm btn-primary" ngf-select ng-model="formValues.TLSKey">Select file</button>
|
||||
<span style="margin-left: 5px;">
|
||||
{{ formValues.TLSKey.name }}
|
||||
<i class="fa fa-times red-icon" ng-if="!formValues.TLSKey" aria-hidden="true"></i>
|
||||
<i class="fa fa-circle-notch fa-spin" ng-if="state.uploadInProgress"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !key-input -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- !tls-options -->
|
||||
<!-- actions -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="state.actionInProgress || !formValues.Name || !formValues.URL || (formValues.TLS && ((formValues.TLSVerify && !formValues.TLSCACert) || (!formValues.TLSSKipClientVerify && (!formValues.TLSCert || !formValues.TLSKey))))"
|
||||
ng-click="createRemoteEndpoint()"
|
||||
button-spinner="state.actionInProgress"
|
||||
>
|
||||
<span ng-hide="state.actionInProgress"><i class="fa fa-bolt" aria-hidden="true"></i> Connect</span>
|
||||
<span ng-show="state.actionInProgress">Connecting...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
<div ng-if="ctrl.formValues.ConnectionType === ctrl.PortainerEndpointConnectionTypes.AZURE">
|
||||
<ng-include src="'app/portainer/views/init/endpoint/includes/azure.html'"></ng-include>
|
||||
</div>
|
||||
<!-- !remote-endpoint -->
|
||||
<div ng-if="ctrl.formValues.ConnectionType === ctrl.PortainerEndpointConnectionTypes.AGENT">
|
||||
<ng-include src="'app/portainer/views/init/endpoint/includes/agent.html'"></ng-include>
|
||||
</div>
|
||||
<!-- !endpoint-type-details -->
|
||||
<!-- actions -->
|
||||
<div class="form-group">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="ctrl.isConnectButtonDisabled()"
|
||||
ng-click="ctrl.createEndpoint()"
|
||||
button-spinner="ctrl.state.actionInProgress"
|
||||
>
|
||||
<span ng-hide="ctrl.state.actionInProgress"><i class="fa fa-bolt" aria-hidden="true"></i> Connect</span>
|
||||
<span ng-show="ctrl.state.actionInProgress">Connecting...</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !actions -->
|
||||
</form>
|
||||
<!-- !init-endpoint form -->
|
||||
</div>
|
||||
|
|
|
@ -1,109 +1,223 @@
|
|||
import _ from 'lodash-es';
|
||||
import angular from 'angular';
|
||||
import { PortainerEndpointInitFormValues, PortainerEndpointInitFormValueEndpointSections } from 'Portainer/models/endpoint/formValues';
|
||||
import { PortainerEndpointTypes, PortainerEndpointConnectionTypes } from 'Portainer/models/endpoint/models';
|
||||
|
||||
angular.module('portainer.app').controller('InitEndpointController', [
|
||||
'$scope',
|
||||
'$state',
|
||||
'EndpointService',
|
||||
'StateManager',
|
||||
'Notifications',
|
||||
function ($scope, $state, EndpointService, StateManager, Notifications) {
|
||||
if (!_.isEmpty($scope.applicationState.endpoint)) {
|
||||
$state.go('portainer.home');
|
||||
require('./includes/localDocker.html');
|
||||
require('./includes/localKubernetes.html');
|
||||
require('./includes/remote.html');
|
||||
require('./includes/azure.html');
|
||||
require('./includes/agent.html');
|
||||
|
||||
class InitEndpointController {
|
||||
/* @ngInject */
|
||||
constructor($async, $scope, $state, EndpointService, EndpointProvider, StateManager, Notifications) {
|
||||
this.$async = $async;
|
||||
this.$scope = $scope;
|
||||
this.$state = $state;
|
||||
this.EndpointService = EndpointService;
|
||||
this.EndpointProvider = EndpointProvider;
|
||||
this.StateManager = StateManager;
|
||||
this.Notifications = Notifications;
|
||||
|
||||
this.createLocalEndpointAsync = this.createLocalEndpointAsync.bind(this);
|
||||
this.createLocalKubernetesEndpointAsync = this.createLocalKubernetesEndpointAsync.bind(this);
|
||||
this.createAgentEndpointAsync = this.createAgentEndpointAsync.bind(this);
|
||||
this.createAzureEndpointAsync = this.createAzureEndpointAsync.bind(this);
|
||||
this.createRemoteEndpointAsync = this.createRemoteEndpointAsync.bind(this);
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
if (!_.isEmpty(this.$scope.applicationState.endpoint)) {
|
||||
this.$state.go('portainer.home');
|
||||
}
|
||||
this.logo = this.StateManager.getState().application.logo;
|
||||
|
||||
$scope.logo = StateManager.getState().application.logo;
|
||||
|
||||
$scope.state = {
|
||||
this.state = {
|
||||
uploadInProgress: false,
|
||||
actionInProgress: false,
|
||||
};
|
||||
|
||||
$scope.formValues = {
|
||||
EndpointType: 'remote',
|
||||
Name: '',
|
||||
URL: '',
|
||||
TLS: false,
|
||||
TLSSkipVerify: false,
|
||||
TLSSKipClientVerify: false,
|
||||
TLSCACert: null,
|
||||
TLSCert: null,
|
||||
TLSKey: null,
|
||||
AzureApplicationId: '',
|
||||
AzureTenantId: '',
|
||||
AzureAuthenticationKey: '',
|
||||
};
|
||||
this.formValues = new PortainerEndpointInitFormValues();
|
||||
this.endpointSections = PortainerEndpointInitFormValueEndpointSections;
|
||||
this.PortainerEndpointConnectionTypes = PortainerEndpointConnectionTypes;
|
||||
}
|
||||
|
||||
$scope.createLocalEndpoint = function () {
|
||||
$scope.state.actionInProgress = true;
|
||||
EndpointService.createLocalEndpoint()
|
||||
.then(function success() {
|
||||
$state.go('portainer.home');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to connect to the Docker environment');
|
||||
})
|
||||
.finally(function final() {
|
||||
$scope.state.actionInProgress = false;
|
||||
});
|
||||
};
|
||||
isRemoteConnectButtonDisabled() {
|
||||
return (
|
||||
this.state.actionInProgress ||
|
||||
!this.formValues.Name ||
|
||||
!this.formValues.URL ||
|
||||
(this.formValues.TLS &&
|
||||
((this.formValues.TLSVerify && !this.formValues.TLSCACert) || (!this.formValues.TLSSKipClientVerify && (!this.formValues.TLSCert || !this.formValues.TLSKey))))
|
||||
);
|
||||
}
|
||||
|
||||
$scope.createAzureEndpoint = function () {
|
||||
var name = $scope.formValues.Name;
|
||||
var applicationId = $scope.formValues.AzureApplicationId;
|
||||
var tenantId = $scope.formValues.AzureTenantId;
|
||||
var authenticationKey = $scope.formValues.AzureAuthenticationKey;
|
||||
isAzureConnectButtonDisabled() {
|
||||
return this.state.actionInProgress || !this.formValues.Name || !this.formValues.AzureApplicationId || !this.formValues.AzureTenantId || !this.formValues.AzureAuthenticationKey;
|
||||
}
|
||||
|
||||
createAzureEndpoint(name, applicationId, tenantId, authenticationKey);
|
||||
};
|
||||
|
||||
$scope.createAgentEndpoint = function () {
|
||||
var name = $scope.formValues.Name;
|
||||
var URL = $scope.formValues.URL;
|
||||
var PublicURL = URL.split(':')[0];
|
||||
|
||||
createRemoteEndpoint(name, 2, URL, PublicURL, true, true, true, null, null, null);
|
||||
};
|
||||
|
||||
$scope.createRemoteEndpoint = function () {
|
||||
var name = $scope.formValues.Name;
|
||||
var URL = $scope.formValues.URL;
|
||||
var PublicURL = URL.split(':')[0];
|
||||
var TLS = $scope.formValues.TLS;
|
||||
var TLSSkipVerify = TLS && $scope.formValues.TLSSkipVerify;
|
||||
var TLSSKipClientVerify = TLS && $scope.formValues.TLSSKipClientVerify;
|
||||
var TLSCAFile = TLSSkipVerify ? null : $scope.formValues.TLSCACert;
|
||||
var TLSCertFile = TLSSKipClientVerify ? null : $scope.formValues.TLSCert;
|
||||
var TLSKeyFile = TLSSKipClientVerify ? null : $scope.formValues.TLSKey;
|
||||
|
||||
createRemoteEndpoint(name, 1, URL, PublicURL, TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile);
|
||||
};
|
||||
|
||||
function createAzureEndpoint(name, applicationId, tenantId, authenticationKey) {
|
||||
$scope.state.actionInProgress = true;
|
||||
EndpointService.createAzureEndpoint(name, applicationId, tenantId, authenticationKey, 1, [])
|
||||
.then(function success() {
|
||||
$state.go('portainer.home');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to connect to the Azure environment');
|
||||
})
|
||||
.finally(function final() {
|
||||
$scope.state.actionInProgress = false;
|
||||
});
|
||||
isConnectButtonDisabled() {
|
||||
switch (this.formValues.ConnectionType) {
|
||||
case PortainerEndpointConnectionTypes.DOCKER_LOCAL:
|
||||
return this.state.actionInProgress;
|
||||
case PortainerEndpointConnectionTypes.KUBERNETES_LOCAL:
|
||||
return this.state.actionInProgress;
|
||||
case PortainerEndpointConnectionTypes.REMOTE:
|
||||
return this.isRemoteConnectButtonDisabled();
|
||||
case PortainerEndpointConnectionTypes.AZURE:
|
||||
return this.isAzureConnectButtonDisabled();
|
||||
case PortainerEndpointConnectionTypes.AGENT:
|
||||
return this.state.actionInProgress || !this.formValues.Name || !this.formValues.URL;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function createRemoteEndpoint(name, type, URL, PublicURL, TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile) {
|
||||
$scope.state.actionInProgress = true;
|
||||
EndpointService.createRemoteEndpoint(name, type, URL, PublicURL, 1, [], TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile)
|
||||
.then(function success() {
|
||||
$state.go('portainer.home');
|
||||
})
|
||||
.catch(function error(err) {
|
||||
Notifications.error('Failure', err, 'Unable to connect to the Docker environment');
|
||||
})
|
||||
.finally(function final() {
|
||||
$scope.state.actionInProgress = false;
|
||||
});
|
||||
createEndpoint() {
|
||||
switch (this.formValues.ConnectionType) {
|
||||
case PortainerEndpointConnectionTypes.DOCKER_LOCAL:
|
||||
return this.createLocalEndpoint();
|
||||
case PortainerEndpointConnectionTypes.KUBERNETES_LOCAL:
|
||||
return this.createLocalKubernetesEndpoint();
|
||||
case PortainerEndpointConnectionTypes.REMOTE:
|
||||
return this.createRemoteEndpoint();
|
||||
case PortainerEndpointConnectionTypes.AZURE:
|
||||
return this.createAzureEndpoint();
|
||||
case PortainerEndpointConnectionTypes.AGENT:
|
||||
return this.createAgentEndpoint();
|
||||
default:
|
||||
this.Notifications.error('Failure', 'Unable to determine wich action to do');
|
||||
}
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* DOCKER_LOCAL (1)
|
||||
*/
|
||||
async createLocalEndpointAsync() {
|
||||
try {
|
||||
this.state.actionInProgress = true;
|
||||
await this.EndpointService.createLocalEndpoint();
|
||||
this.$state.go('portainer.home');
|
||||
} catch (err) {
|
||||
this.Notifications.error('Failure', err, 'Unable to connect to the Docker environment');
|
||||
} finally {
|
||||
this.state.actionInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
createLocalEndpoint() {
|
||||
return this.$async(this.createLocalEndpointAsync);
|
||||
}
|
||||
|
||||
/**
|
||||
* KUBERNETES_LOCAL (5)
|
||||
*/
|
||||
async createLocalKubernetesEndpointAsync() {
|
||||
try {
|
||||
this.state.actionInProgress = true;
|
||||
const endpoint = await this.EndpointService.createLocalKubernetesEndpoint();
|
||||
this.$state.go('portainer.endpoints.endpoint.kubernetesConfig', { id: endpoint.Id });
|
||||
} catch (err) {
|
||||
this.Notifications.error('Failure', err, 'Unable to connect to the Kubernetes environment');
|
||||
} finally {
|
||||
this.state.actionInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
createLocalKubernetesEndpoint() {
|
||||
return this.$async(this.createLocalKubernetesEndpointAsync);
|
||||
}
|
||||
|
||||
/**
|
||||
* DOCKER / KUBERNETES AGENT (2 / 6)
|
||||
*/
|
||||
async createAgentEndpointAsync() {
|
||||
try {
|
||||
this.state.actionInProgress = true;
|
||||
const name = this.formValues.Name;
|
||||
const URL = this.formValues.URL;
|
||||
const PublicURL = URL.split(':')[0];
|
||||
// TODO: k8s merge - change type ID for agent on kube (6) or agent on swarm (2)
|
||||
const endpoint = await this.EndpointService.createRemoteEndpoint(
|
||||
name,
|
||||
PortainerEndpointTypes.AgentOnKubernetesEnvironment,
|
||||
URL,
|
||||
PublicURL,
|
||||
1,
|
||||
[],
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
// TODO: k8s merge - go on home whith agent on swarm (2)
|
||||
this.$state.go('portainer.endpoints.endpoint.kubernetesConfig', { id: endpoint.Id });
|
||||
} catch (err) {
|
||||
this.Notifications.error('Failure', err, 'Unable to connect to the Docker environment');
|
||||
} finally {
|
||||
this.state.actionInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
createAgentEndpoint() {
|
||||
return this.$async(this.createAgentEndpointAsync);
|
||||
}
|
||||
|
||||
/**
|
||||
* DOCKER REMOTE (1)
|
||||
*/
|
||||
async createRemoteEndpointAsync() {
|
||||
try {
|
||||
this.state.actionInProgress = true;
|
||||
const name = this.formValues.Name;
|
||||
const type = PortainerEndpointTypes.DockerEnvironment;
|
||||
const URL = this.formValues.URL;
|
||||
const PublicURL = URL.split(':')[0];
|
||||
const TLS = this.formValues.TLS;
|
||||
const TLSSkipVerify = TLS && this.formValues.TLSSkipVerify;
|
||||
const TLSSKipClientVerify = TLS && this.formValues.TLSSKipClientVerify;
|
||||
const TLSCAFile = TLSSkipVerify ? null : this.formValues.TLSCACert;
|
||||
const TLSCertFile = TLSSKipClientVerify ? null : this.formValues.TLSCert;
|
||||
const TLSKeyFile = TLSSKipClientVerify ? null : this.formValues.TLSKey;
|
||||
await this.EndpointService.createRemoteEndpoint(name, type, URL, PublicURL, 1, [], TLS, TLSSkipVerify, TLSSKipClientVerify, TLSCAFile, TLSCertFile, TLSKeyFile);
|
||||
this.$state.go('portainer.home');
|
||||
} catch (err) {
|
||||
this.Notifications.error('Failure', err, 'Unable to connect to the Docker environment');
|
||||
} finally {
|
||||
this.state.actionInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
createRemoteEndpoint() {
|
||||
return this.$async(this.createAgentEndpointAsync);
|
||||
}
|
||||
|
||||
/**
|
||||
* AZURE (4)
|
||||
*/
|
||||
async createAzureEndpointAsync() {
|
||||
try {
|
||||
this.state.actionInProgress = true;
|
||||
var name = this.formValues.Name;
|
||||
var applicationId = this.formValues.AzureApplicationId;
|
||||
var tenantId = this.formValues.AzureTenantId;
|
||||
var authenticationKey = this.formValues.AzureAuthenticationKey;
|
||||
await this.EndpointService.createAzureEndpoint(name, applicationId, tenantId, authenticationKey, 1, []);
|
||||
this.$state.go('portainer.home');
|
||||
} catch (err) {
|
||||
this.Notifications.error('Failure', err, 'Unable to connect to the Azure environment');
|
||||
} finally {
|
||||
this.state.actionInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
createAzureEndpoint() {
|
||||
return this.$async(this.createAgentEndpointAsync);
|
||||
}
|
||||
}
|
||||
|
||||
export default InitEndpointController;
|
||||
angular.module('portainer.app').controller('InitEndpointController', InitEndpointController);
|
||||
|
|
16
app/portainer/views/logout/logout.html
Normal file
16
app/portainer/views/logout/logout.html
Normal file
|
@ -0,0 +1,16 @@
|
|||
<div class="page-wrapper">
|
||||
<!-- login box -->
|
||||
<div class="container simple-box">
|
||||
<div class="col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3">
|
||||
<!-- login box logo -->
|
||||
<div class="row">
|
||||
<img ng-if="!ctrl.logo" src="~@/assets/images/logo_alt.png" class="simple-box-logo" alt="Portainer" />
|
||||
<img ng-if="ctrl.logo" ng-src="{{ ctrl.logo }}" class="simple-box-logo" />
|
||||
</div>
|
||||
<div class="row" style="text-align: center;">
|
||||
Logout in progress...
|
||||
<i class="fa fa-cog fa-spin" style="margin-left: 5px;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
61
app/portainer/views/logout/logoutController.js
Normal file
61
app/portainer/views/logout/logoutController.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
import angular from 'angular';
|
||||
|
||||
class LogoutController {
|
||||
/* @ngInject */
|
||||
constructor($async, $state, $transition$, Authentication, StateManager, Notifications, LocalStorage) {
|
||||
this.$async = $async;
|
||||
this.$state = $state;
|
||||
this.$transition$ = $transition$;
|
||||
|
||||
this.Authentication = Authentication;
|
||||
this.StateManager = StateManager;
|
||||
this.Notifications = Notifications;
|
||||
this.LocalStorage = LocalStorage;
|
||||
|
||||
this.logo = this.StateManager.getState().application.logo;
|
||||
this.logoutAsync = this.logoutAsync.bind(this);
|
||||
|
||||
this.onInit = this.onInit.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* UTILS FUNCTIONS SECTION
|
||||
*/
|
||||
async logoutAsync() {
|
||||
const error = this.$transition$.params().error;
|
||||
const performApiLogout = this.$transition$.params().performApiLogout;
|
||||
try {
|
||||
await this.Authentication.logout(performApiLogout);
|
||||
} finally {
|
||||
this.LocalStorage.storeLogoutReason(error);
|
||||
this.$state.go('portainer.auth', { reload: true });
|
||||
}
|
||||
}
|
||||
|
||||
logout() {
|
||||
return this.$async(this.logoutAsync);
|
||||
}
|
||||
|
||||
/**
|
||||
* END UTILS FUNCTIONS SECTION
|
||||
*/
|
||||
|
||||
async onInit() {
|
||||
try {
|
||||
await this.logout();
|
||||
} catch (err) {
|
||||
this.Notifications.error('Failure', err, 'An error occured during logout');
|
||||
}
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
return this.$async(this.onInit);
|
||||
}
|
||||
|
||||
/**
|
||||
* END ON INIT SECTION
|
||||
*/
|
||||
}
|
||||
|
||||
export default LogoutController;
|
||||
angular.module('portainer.app').controller('LogoutController', LogoutController);
|
|
@ -13,9 +13,11 @@
|
|||
<a ui-sref="portainer.home" ui-sref-active="active">Home <span class="menu-icon fa fa-home fa-fw"></span></a>
|
||||
</li>
|
||||
<li class="sidebar-title endpoint-name" ng-if="applicationState.endpoint.name"> <span class="fa fa-plug space-right"></span>{{ applicationState.endpoint.name }} </li>
|
||||
<kubernetes-sidebar-content ng-if="applicationState.endpoint.mode && applicationState.endpoint.mode.provider === 'KUBERNETES'" admin-access="isAdmin">
|
||||
</kubernetes-sidebar-content>
|
||||
<azure-sidebar-content ng-if="applicationState.endpoint.mode && applicationState.endpoint.mode.provider === 'AZURE'"> </azure-sidebar-content>
|
||||
<docker-sidebar-content
|
||||
ng-if="applicationState.endpoint.mode && applicationState.endpoint.mode.provider !== 'AZURE'"
|
||||
ng-if="applicationState.endpoint.mode && applicationState.endpoint.mode.provider !== 'AZURE' && applicationState.endpoint.mode.provider !== 'KUBERNETES'"
|
||||
endpoint-api-version="applicationState.endpoint.apiVersion"
|
||||
swarm-management="applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE' && applicationState.endpoint.mode.role === 'MANAGER'"
|
||||
standalone-management="applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE'"
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6 nopadding">
|
||||
<input type="text" id="filter" ng-model="state.filterUsers" placeholder="Filter..." class="form-control input-sm" />
|
||||
<input type="text" id="filter-users" ng-model="state.filterUsers" placeholder="Filter..." class="form-control input-sm" />
|
||||
</div>
|
||||
</rd-widget-taskbar>
|
||||
<rd-widget-body classes="no-padding">
|
||||
|
@ -123,7 +123,7 @@
|
|||
>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6 nopadding">
|
||||
<input type="text" id="filter" ng-model="state.filterGroupMembers" placeholder="Filter..." class="form-control input-sm" />
|
||||
<input type="text" id="filter-group" ng-model="state.filterGroupMembers" placeholder="Filter..." class="form-control input-sm" />
|
||||
</div>
|
||||
</rd-widget-taskbar>
|
||||
<rd-widget-body classes="no-padding">
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue