From a5058e8f1e49d5108bddad7f85592273f6c8c675 Mon Sep 17 00:00:00 2001 From: Felix Han Date: Tue, 31 Aug 2021 11:10:22 +1200 Subject: [PATCH] feat(k8s): front end backport to CE --- .../views/applications/applications.js | 1 + .../applications/applicationsController.js | 6 +- .../create/createApplication.html | 6 +- app/kubernetes/views/deploy/deploy.html | 36 +++------- .../views/deploy/deployController.js | 70 ++++++++++++++----- .../git-form-compose-path-field.html | 4 +- .../git-form-compose-path-field/index.js | 2 + .../git-form-info-panel.html | 4 +- .../git-form/git-form-info-panel/index.js | 1 + .../components/forms/git-form/git-form.html | 7 +- .../components/forms/git-form/git-form.js | 2 + .../kubernetes-app-git-form.js | 13 ---- ...netes-redeploy-app-git-form.controller.js} | 48 +++++++++++-- .../kubernetes-redeploy-app-git-form.html} | 3 +- .../kubernetes-redeploy-app-git-form.js | 13 ++++ app/portainer/services/api/stackService.js | 10 +++ .../views/stacks/create/createstack.html | 2 + 17 files changed, 159 insertions(+), 69 deletions(-) delete mode 100644 app/portainer/components/forms/kubernetes-app-git-form/kubernetes-app-git-form.js rename app/portainer/components/forms/{kubernetes-app-git-form/kubernetes-app-git-form.controller.js => kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.controller.js} (58%) rename app/portainer/components/forms/{kubernetes-app-git-form/kubernetes-app-git-form.html => kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.html} (91%) create mode 100644 app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.js diff --git a/app/kubernetes/views/applications/applications.js b/app/kubernetes/views/applications/applications.js index 7994db0eb..da0a7ff07 100644 --- a/app/kubernetes/views/applications/applications.js +++ b/app/kubernetes/views/applications/applications.js @@ -4,5 +4,6 @@ angular.module('portainer.kubernetes').component('kubernetesApplicationsView', { controllerAs: 'ctrl', bindings: { $transition$: '<', + endpoint: '<', }, }); diff --git a/app/kubernetes/views/applications/applicationsController.js b/app/kubernetes/views/applications/applicationsController.js index d7896d42d..3d3d34556 100644 --- a/app/kubernetes/views/applications/applicationsController.js +++ b/app/kubernetes/views/applications/applicationsController.js @@ -7,7 +7,7 @@ import KubernetesApplicationHelper from 'Kubernetes/helpers/application'; class KubernetesApplicationsController { /* @ngInject */ - constructor($async, $state, Notifications, KubernetesApplicationService, Authentication, ModalService, LocalStorage) { + constructor($async, $state, Notifications, KubernetesApplicationService, Authentication, ModalService, LocalStorage, StackService) { this.$async = $async; this.$state = $state; this.Notifications = Notifications; @@ -16,6 +16,7 @@ class KubernetesApplicationsController { this.Authentication = Authentication; this.ModalService = ModalService; this.LocalStorage = LocalStorage; + this.StackService = StackService; this.onInit = this.onInit.bind(this); this.getApplications = this.getApplications.bind(this); @@ -66,6 +67,9 @@ class KubernetesApplicationsController { for (const application of selectedItems) { try { await this.KubernetesApplicationService.delete(application); + if (application.StackId) { + await this.StackService.remove({ Id: application.StackId }, false, this.endpoint.Id); + } this.Notifications.success('Application successfully removed', application.Name); const index = this.applications.indexOf(application); this.applications.splice(index, 1); diff --git a/app/kubernetes/views/applications/create/createApplication.html b/app/kubernetes/views/applications/create/createApplication.html index ec9ed0002..5f4a52c99 100644 --- a/app/kubernetes/views/applications/create/createApplication.html +++ b/app/kubernetes/views/applications/create/createApplication.html @@ -21,6 +21,8 @@ class-name="text-muted" url="ctrl.stack.GitConfig.URL" config-file-path="ctrl.stack.GitConfig.ConfigFilePath" + additional-files="ctrl.stack.AdditionalFiles" + type="application" >
Namespace @@ -55,11 +57,11 @@ - + > diff --git a/app/kubernetes/views/deploy/deploy.html b/app/kubernetes/views/deploy/deploy.html index 237acec2c..eb2ca19d7 100644 --- a/app/kubernetes/views/deploy/deploy.html +++ b/app/kubernetes/views/deploy/deploy.html @@ -34,32 +34,16 @@ -
-
- Git repository -
- - -
- - Indicate the path to the yaml file from the root of your repository. - -
-
- -
- -
-
- -
+ diff --git a/app/kubernetes/views/deploy/deployController.js b/app/kubernetes/views/deploy/deployController.js index a22329aaa..48d120961 100644 --- a/app/kubernetes/views/deploy/deployController.js +++ b/app/kubernetes/views/deploy/deployController.js @@ -1,20 +1,22 @@ import angular from 'angular'; import _ from 'lodash-es'; import stripAnsi from 'strip-ansi'; - +import uuidv4 from 'uuid/v4'; import { KubernetesDeployManifestTypes, KubernetesDeployBuildMethods, KubernetesDeployRequestMethods } from 'Kubernetes/models/deploy'; import { buildOption } from '@/portainer/components/box-selector'; class KubernetesDeployController { /* @ngInject */ - constructor($async, $state, $window, ModalService, Notifications, EndpointProvider, KubernetesResourcePoolService, StackService) { + constructor($async, $state, $window, $analytics, ModalService, Notifications, EndpointProvider, KubernetesResourcePoolService, StackService, WebhookHelper) { this.$async = $async; this.$state = $state; this.$window = $window; + this.$analytics = $analytics; this.ModalService = ModalService; this.Notifications = Notifications; this.EndpointProvider = EndpointProvider; this.KubernetesResourcePoolService = KubernetesResourcePoolService; this.StackService = StackService; + this.WebhookHelper = WebhookHelper; this.deployOptions = [ buildOption('method_kubernetes', 'fa fa-cubes', 'Kubernetes', 'Kubernetes manifest format', KubernetesDeployManifestTypes.KUBERNETES), @@ -35,7 +37,19 @@ class KubernetesDeployController { isEditorDirty: false, }; - this.formValues = {}; + this.formValues = { + RepositoryURL: '', + RepositoryReferenceName: '', + RepositoryAuthentication: true, + RepositoryUsername: '', + RepositoryPassword: '', + AdditionalFiles: [], + ComposeFilePathInRepository: 'deployment.yml', + RepositoryAutomaticUpdates: true, + RepositoryMechanism: 'Interval', + RepositoryFetchInterval: '5m', + RepositoryWebhookURL: this.WebhookHelper.returnStackWebhookUrl(uuidv4()), + }; this.ManifestDeployTypes = KubernetesDeployManifestTypes; this.BuildMethods = KubernetesDeployBuildMethods; this.endpointId = this.EndpointProvider.endpointID(); @@ -45,16 +59,12 @@ class KubernetesDeployController { this.onChangeFileContent = this.onChangeFileContent.bind(this); this.getNamespacesAsync = this.getNamespacesAsync.bind(this); this.onChangeFormValues = this.onChangeFormValues.bind(this); - this.onRepoUrlChange = this.onRepoUrlChange.bind(this); - this.onRepoRefChange = this.onRepoRefChange.bind(this); } disableDeploy() { const isGitFormInvalid = this.state.BuildMethod === KubernetesDeployBuildMethods.GIT && - (!this.formValues.RepositoryURL || - !this.formValues.FilePathInRepository || - (this.formValues.RepositoryAuthentication && (!this.formValues.RepositoryUsername || !this.formValues.RepositoryPassword))); + (!this.formValues.RepositoryURL || !this.formValues.FilePathInRepository || (this.formValues.RepositoryAuthentication && !this.formValues.RepositoryPassword)); const isWebEditorInvalid = this.state.BuildMethod === KubernetesDeployBuildMethods.WEB_EDITOR && _.isEmpty(this.formValues.EditorContent); return isGitFormInvalid || isWebEditorInvalid || _.isEmpty(this.formValues.Namespace) || this.state.actionInProgress; @@ -67,14 +77,6 @@ class KubernetesDeployController { }; } - onRepoUrlChange(value) { - this.onChangeFormValues({ RepositoryURL: value }); - } - - onRepoRefChange(value) { - this.onChangeFormValues({ RepositoryReferenceName: value }); - } - onChangeFileContent(value) { this.formValues.EditorContent = value; this.state.isEditorDirty = true; @@ -91,6 +93,11 @@ class KubernetesDeployController { this.state.actionInProgress = true; try { + //Analytics + const metadata = { + format: this.state.DeployType === this.ManifestDeployTypes.COMPOSE ? 'compose' : 'manifest', + }; + const method = this.state.BuildMethod === this.BuildMethods.GIT ? KubernetesDeployRequestMethods.REPOSITORY : KubernetesDeployRequestMethods.STRING; const payload = { @@ -99,6 +106,7 @@ class KubernetesDeployController { }; if (method === KubernetesDeployRequestMethods.REPOSITORY) { + metadata.type = 'git'; payload.RepositoryURL = this.formValues.RepositoryURL; payload.RepositoryReferenceName = this.formValues.RepositoryReferenceName; payload.RepositoryAuthentication = this.formValues.RepositoryAuthentication ? true : false; @@ -106,11 +114,26 @@ class KubernetesDeployController { payload.RepositoryUsername = this.formValues.RepositoryUsername; payload.RepositoryPassword = this.formValues.RepositoryPassword; } - payload.FilePathInRepository = this.formValues.FilePathInRepository; + payload.ManifestFile = this.formValues.ComposeFilePathInRepository; + payload.AdditionalFiles = this.formValues.AdditionalFiles; + if (this.formValues.RepositoryAutomaticUpdates) { + payload.AutoUpdate = {}; + if (this.formValues.RepositoryMechanism === `Interval`) { + payload.AutoUpdate.Interval = this.formValues.RepositoryFetchInterval; + metadata['automatic-updates'] = 'polling'; + } else if (this.formValues.RepositoryMechanism === `Webhook`) { + payload.AutoUpdate.Webhook = this.formValues.RepositoryWebhookURL; + metadata['automatic-updates'] = 'webhook'; + } + } else { + metadata['automatic-updates'] = 'off'; + } } else { + metadata.type = 'web-editor'; payload.StackFileContent = this.formValues.EditorContent; } + this.$analytics.eventTrack('kubernetes-application-advanced-deployment', { category: 'kubernetes', metadata: metadata }); await this.StackService.kubernetesDeploy(this.endpointId, method, payload); this.Notifications.success('Manifest successfully deployed'); @@ -158,6 +181,19 @@ class KubernetesDeployController { } } async onInit() { + this.state = { + DeployType: KubernetesDeployManifestTypes.KUBERNETES, + BuildMethod: KubernetesDeployBuildMethods.GIT, + tabLogsDisabled: true, + activeTab: 0, + viewReady: false, + isEditorDirty: false, + }; + + this.ManifestDeployTypes = KubernetesDeployManifestTypes; + this.BuildMethods = KubernetesDeployBuildMethods; + this.endpointId = this.EndpointProvider.endpointID(); + await this.getNamespaces(); this.state.viewReady = true; diff --git a/app/portainer/components/forms/git-form/git-form-compose-path-field/git-form-compose-path-field.html b/app/portainer/components/forms/git-form/git-form-compose-path-field/git-form-compose-path-field.html index 541023c49..f71bc2fba 100644 --- a/app/portainer/components/forms/git-form/git-form-compose-path-field/git-form-compose-path-field.html +++ b/app/portainer/components/forms/git-form/git-form-compose-path-field/git-form-compose-path-field.html @@ -4,8 +4,8 @@
- +
- +
diff --git a/app/portainer/components/forms/git-form/git-form-compose-path-field/index.js b/app/portainer/components/forms/git-form/git-form-compose-path-field/index.js index 7906d0e85..0013ff5a2 100644 --- a/app/portainer/components/forms/git-form/git-form-compose-path-field/index.js +++ b/app/portainer/components/forms/git-form/git-form-compose-path-field/index.js @@ -1,6 +1,8 @@ export const gitFormComposePathField = { templateUrl: './git-form-compose-path-field.html', bindings: { + textTitle: '@', + placeholder: '@', value: '<', onChange: '<', }, diff --git a/app/portainer/components/forms/git-form/git-form-info-panel/git-form-info-panel.html b/app/portainer/components/forms/git-form/git-form-info-panel/git-form-info-panel.html index ae8c95252..171381eba 100644 --- a/app/portainer/components/forms/git-form/git-form-info-panel/git-form-info-panel.html +++ b/app/portainer/components/forms/git-form/git-form-info-panel/git-form-info-panel.html @@ -1,7 +1,7 @@

- This stack was deployed from the git repository {{ $ctrl.url }} + This {{ $ctrl.type }} was deployed from the git repository {{ $ctrl.url }} .

@@ -9,7 +9,7 @@ {{ $ctrl.configFilePath }},{{ $ctrl.additionalFiles.join(',') }} - in git and pull from here to update the stack. + in git and pull from here to update the {{ $ctrl.type }}.

diff --git a/app/portainer/components/forms/git-form/git-form-info-panel/index.js b/app/portainer/components/forms/git-form/git-form-info-panel/index.js index c2529969b..9cfe1c261 100644 --- a/app/portainer/components/forms/git-form/git-form-info-panel/index.js +++ b/app/portainer/components/forms/git-form/git-form-info-panel/index.js @@ -5,5 +5,6 @@ export const gitFormInfoPanel = { configFilePath: '<', additionalFiles: '<', className: '@', + type: '@', }, }; diff --git a/app/portainer/components/forms/git-form/git-form.html b/app/portainer/components/forms/git-form/git-form.html index ba35fcf67..ac7bb699b 100644 --- a/app/portainer/components/forms/git-form/git-form.html +++ b/app/portainer/components/forms/git-form/git-form.html @@ -4,7 +4,12 @@ - + diff --git a/app/portainer/components/forms/git-form/git-form.js b/app/portainer/components/forms/git-form/git-form.js index affa081dc..5dae425b5 100644 --- a/app/portainer/components/forms/git-form/git-form.js +++ b/app/portainer/components/forms/git-form/git-form.js @@ -4,6 +4,8 @@ export const gitForm = { templateUrl: './git-form.html', controller, bindings: { + pathTextTitle: '@', + pathPlaceholder: '@', model: '<', onChange: '<', additionalFile: '<', diff --git a/app/portainer/components/forms/kubernetes-app-git-form/kubernetes-app-git-form.js b/app/portainer/components/forms/kubernetes-app-git-form/kubernetes-app-git-form.js deleted file mode 100644 index 7a21a6384..000000000 --- a/app/portainer/components/forms/kubernetes-app-git-form/kubernetes-app-git-form.js +++ /dev/null @@ -1,13 +0,0 @@ -import angular from 'angular'; -import controller from './kubernetes-app-git-form.controller'; - -const kubernetesAppGitForm = { - templateUrl: './kubernetes-app-git-form.html', - controller, - bindings: { - namespace: '<', - stack: '<', - }, -}; - -angular.module('portainer.app').component('kubernetesAppGitForm', kubernetesAppGitForm); diff --git a/app/portainer/components/forms/kubernetes-app-git-form/kubernetes-app-git-form.controller.js b/app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.controller.js similarity index 58% rename from app/portainer/components/forms/kubernetes-app-git-form/kubernetes-app-git-form.controller.js rename to app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.controller.js index b2ec4c90a..08774e8a9 100644 --- a/app/portainer/components/forms/kubernetes-app-git-form/kubernetes-app-git-form.controller.js +++ b/app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.controller.js @@ -1,16 +1,18 @@ -class KubernetesAppGitFormController { +import uuidv4 from 'uuid/v4'; +class KubernetesRedeployAppGitFormController { /* @ngInject */ - constructor($async, $state, StackService, ModalService, Notifications) { + constructor($async, $state, $analytics, StackService, ModalService, Notifications, WebhookHelper) { this.$async = $async; this.$state = $state; this.StackService = StackService; this.ModalService = ModalService; this.Notifications = Notifications; + this.WebhookHelper = WebhookHelper; this.state = { saveGitSettingsInProgress: false, redeployInProgress: false, - showConfig: true, + showConfig: false, isEdit: false, }; @@ -19,6 +21,13 @@ class KubernetesAppGitFormController { RepositoryAuthentication: false, RepositoryUsername: '', RepositoryPassword: '', + // auto upadte + AutoUpdate: { + RepositoryAutomaticUpdates: false, + RepositoryMechanism: 'Interval', + RepositoryFetchInterval: '5m', + RepositoryWebhookURL: '', + }, }; this.onChange = this.onChange.bind(this); @@ -39,6 +48,20 @@ class KubernetesAppGitFormController { async pullAndRedeployApplication() { return this.$async(async () => { try { + //Analytics + const metadata = {}; + + if (this.formValues.AutoUpdate.RepositoryAutomaticUpdates) { + if (this.formValues.AutoUpdate.RepositoryMechanism === `Interval`) { + metadata['automatic-updates'] = 'polling'; + } else if (this.formValues.AutoUpdate.RepositoryMechanism === `Webhook`) { + metadata['automatic-updates'] = 'webhook'; + } + } else { + metadata['automatic-updates'] = 'off'; + } + this.$analytics.eventTrack('kubernetes-application-edit', { category: 'kubernetes', metadata: metadata }); + const confirmed = await this.ModalService.confirmAsync({ title: 'Are you sure?', message: 'Any changes to this application will be overriden by the definition in git and may cause a service interruption. Do you wish to continue', @@ -84,6 +107,23 @@ class KubernetesAppGitFormController { $onInit() { this.formValues.RefName = this.stack.GitConfig.ReferenceName; + // Init auto update + if (this.stack.AutoUpdate && (this.stack.AutoUpdate.Interval || this.stack.AutoUpdate.Webhook)) { + this.formValues.AutoUpdate.RepositoryAutomaticUpdates = true; + + if (this.stack.AutoUpdate.Interval) { + this.formValues.AutoUpdate.RepositoryMechanism = `Interval`; + this.formValues.AutoUpdate.RepositoryFetchInterval = this.stack.AutoUpdate.Interval; + } else if (this.stack.AutoUpdate.Webhook) { + this.formValues.AutoUpdate.RepositoryMechanism = `Webhook`; + this.formValues.AutoUpdate.RepositoryWebhookURL = this.WebhookHelper.returnStackWebhookUrl(this.stack.AutoUpdate.Webhook); + } + } + + if (!this.formValues.AutoUpdate.RepositoryWebhookURL) { + this.formValues.AutoUpdate.RepositoryWebhookURL = this.WebhookHelper.returnStackWebhookUrl(uuidv4()); + } + if (this.stack.GitConfig && this.stack.GitConfig.Authentication) { this.formValues.RepositoryUsername = this.stack.GitConfig.Authentication.Username; this.formValues.RepositoryAuthentication = true; @@ -92,4 +132,4 @@ class KubernetesAppGitFormController { } } -export default KubernetesAppGitFormController; +export default KubernetesRedeployAppGitFormController; diff --git a/app/portainer/components/forms/kubernetes-app-git-form/kubernetes-app-git-form.html b/app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.html similarity index 91% rename from app/portainer/components/forms/kubernetes-app-git-form/kubernetes-app-git-form.html rename to app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.html index 18e2a2f74..1266464ed 100644 --- a/app/portainer/components/forms/kubernetes-app-git-form/kubernetes-app-git-form.html +++ b/app/portainer/components/forms/kubernetes-redeploy-app-git-form/kubernetes-redeploy-app-git-form.html @@ -9,7 +9,7 @@

- +

@@ -36,6 +36,7 @@