From 3e878c5449ae42a345c078212ad635360ace2406 Mon Sep 17 00:00:00 2001 From: Robert Patrick Date: Tue, 28 Feb 2023 16:50:30 -0600 Subject: [PATCH] fixing Verrazzano App Status Checks to work across clusters --- electron/app/js/ipcRendererPreload.js | 1 + electron/app/js/wktWindow.js | 17 +- electron/app/locales/en/electron.json | 3 + electron/app/locales/en/webui.json | 21 ++- webui/src/js/utils/k8s-helper.js | 74 +++++++-- webui/src/js/utils/view-helper.js | 1 - .../js/utils/vz-application-status-checker.js | 76 ++++++++- .../k8s-helper-choose-cluster-dialog.js | 52 ++++++ webui/src/js/viewModels/kubectl-page.js | 15 -- .../viewModels/vz-application-design-view.js | 33 +++- ...pplication-status-choose-cluster-dialog.js | 52 ++++++ .../vz-application-status-dialog.js | 4 +- .../js/viewModels/vz-install-design-view.js | 2 +- .../views/choose-kubectl-context-dialog.html | 2 +- .../k8s-helper-choose-cluster-dialog.html | 31 ++++ webui/src/js/views/kubectl-page.html | 153 ++++++++---------- ...lication-status-choose-cluster-dialog.html | 31 ++++ webui/src/js/windowStateUtils.js | 11 +- 18 files changed, 445 insertions(+), 134 deletions(-) create mode 100644 webui/src/js/viewModels/k8s-helper-choose-cluster-dialog.js create mode 100644 webui/src/js/viewModels/vz-application-status-choose-cluster-dialog.js create mode 100644 webui/src/js/views/k8s-helper-choose-cluster-dialog.html create mode 100644 webui/src/js/views/vz-application-status-choose-cluster-dialog.html diff --git a/electron/app/js/ipcRendererPreload.js b/electron/app/js/ipcRendererPreload.js index f02f36895..8e757725e 100644 --- a/electron/app/js/ipcRendererPreload.js +++ b/electron/app/js/ipcRendererPreload.js @@ -92,6 +92,7 @@ contextBridge.exposeInMainWorld( 'start-verrazzano-install', 'start-get-verrazzano-install-status', 'start-deploy-verrazzano-component', + 'start-get-vz-application-status', 'start-undeploy-verrazzano-component', 'start-deploy-verrazzano-application', 'start-undeploy-verrazzano-application', diff --git a/electron/app/js/wktWindow.js b/electron/app/js/wktWindow.js index 9f0585676..05adf766e 100644 --- a/electron/app/js/wktWindow.js +++ b/electron/app/js/wktWindow.js @@ -408,7 +408,7 @@ class WktAppMenu { if (!focusedWindow) { return dialog.showErrorBox( i18n.t('menu-go-domain-status-title'), - i18n.t('menu-go-domain=status-error-message') + i18n.t('menu-go-domain-status-error-message') ); } sendToWindow(focusedWindow, 'start-get-k8s-domain-status'); @@ -550,6 +550,21 @@ class WktAppMenu { sendToWindow(focusedWindow,'start-deploy-verrazzano-application'); } }, + { + id: 'checkVerrazzanoApplicationStatus', + label: i18n.t('menu-go-check-verrazzano-application-status'), + visible: !this._isWkoTarget, + enabled: !this._hasOpenDialog, + click(item, focusedWindow) { + if (!focusedWindow) { + return dialog.showErrorBox( + i18n.t('menu-go-check-verrazzano-application-status-error-title'), + i18n.t('menu-go-check-verrazzano-application-status-error-message') + ); + } + sendToWindow(focusedWindow, 'start-get-vz-application-status'); + } + }, { id: 'undeployVerrazzanoApplication', label: i18n.t('menu-go-undeploy-verrazzano-application'), diff --git a/electron/app/locales/en/electron.json b/electron/app/locales/en/electron.json index 67279e05e..6141c9e73 100644 --- a/electron/app/locales/en/electron.json +++ b/electron/app/locales/en/electron.json @@ -93,6 +93,9 @@ "menu-go-deploy-verrazzano-application": "Deploy Verrazzano Application", "menu-go-deploy-verrazzano-application-error-title": "No Active Project Window", "menu-go-deploy-verrazzano-application-error-message": "Cannot deploy Verrazzano application since there is no active window, and therefore no project with the Verrazzano application configuration to deploy.", + "menu-go-check-verrazzano-application-status": "Check Verrazzano Application Status", + "menu-go-check-verrazzano-application-status-error-title": "No Active Project Window", + "menu-go-check-verrazzano-application-status-error-message": "Cannot check Verrazzano application status since there is no active window, and therefore no project with the Verrazzano application configuration to check.", "menu-go-undeploy-verrazzano-application": "Undeploy Verrazzano Application", "menu-go-undeploy-verrazzano-application-error-title": "No Active Project Window", "menu-go-undeploy-verrazzano-application-error-message": "Cannot undeploy Verrazzano application since there is no active window, and therefore no project with the Verrazzano application configuration to undeploy.", diff --git a/electron/app/locales/en/webui.json b/electron/app/locales/en/webui.json index f6681d46c..cc6866109 100644 --- a/electron/app/locales/en/webui.json +++ b/electron/app/locales/en/webui.json @@ -102,11 +102,14 @@ "kubectl-helper-project-not-saved-error-prefix": "Unable to verify Kubernetes client connectivity because project save failed", "kubectl-helper-set-context-error-message": "Unable to verify Kubernetes client connectivity because setting the Kubernetes client cluster context failed: {{error}}.", "kubectl-helper-verify-connect-in-progress": "Verifying Kubernetes Client Connectivity", - "kubectl-helper-verify-connect-success-title": "Verify Kubernetes Client Connectivity Success", - "kubectl-helper-verify-connect-success-message": "Kubernetes client (version {{clientVersion}}) successfully connected to the Kubernetes cluster (version {{serverVersion}}).", - "kubectl-helper-verify-connect-failed-title": "Verify Kubernetes Client Connectivity Failed", - "kubectl-helper-verify-connect-failed-error-message": "Failed to verify Kubernetes client connectivity: {{error}}.", + "kubectl-helper-verify-connect-success-title": "Verify Kubernetes Client Connectivity Success to Cluster {{clusterName}}", + "kubectl-helper-verify-connect-success-message": "Kubernetes client (version {{clientVersion}}) successfully connected to the Kubernetes cluster {{clusterName}} (version {{serverVersion}}).", + "kubectl-helper-verify-connect-failed-title": "Verify Kubernetes Client Connectivity Failed for Cluster {{clusterName}}", + "kubectl-helper-verify-connect-failed-error-message": "Failed to verify Kubernetes client connectivity to cluster {{clusterName}}: {{error}}.", "kubectl-helper-verify-connect-catch-all-error-message": "Failed to verify Kubernetes client connectivity due to an unexpected error: {{error}}.", + "kubectl-helper-choose-cluster-dialog-title": "Choose the Verrazzano cluster to verify connectivity", + "kubectl-helper-choose-cluster-name-label": "Verrazzano Cluster Name", + "kubectl-helper-choose-cluster-name-help": "The Verrazzano cluster name for which to verify connectivity.", "project-settings-form-name": "Project Settings", "project-settings-title": "Project Settings", @@ -1642,6 +1645,7 @@ "vz-application-design-choose-clusters-flow-nane": "Choose Verrazzano Clusters", "vz-application-design-choose-clusters-validation-error-title": "Choose Verrazzano Clusters for Application Placement Aborted", "vz-application-design-get-clusters-in-progress": "Getting Verrazzano Clusters", + "vz-application-design-use-context-error-message": "Unable to choose clusters due to an error while switching the Kubernetes cluster context: {{error}}.", "vz-application-design-get-clusters-error-message": "Unable to choose clusters due to an error while getting the list of available clusters: {{error}}.", "vz-application-design-choose-clusters-dialog-title": "Choose Verrazzano Clusters for Application Placement", "vz-application-design-choose-clusters-name-label": "Verrazzano Cluster Names", @@ -1772,8 +1776,15 @@ "vz-application-status-checker-application-not-exist-error-message": "Unable to get the application status because the application {{application}} does not exist.", "vz-application-status-checker-ns-not-exist-error-message": "Unable to get the application status because the application namespace {{namespace}} does not exist.", "vz-application-status-checker-domain-not-exist-error-message": "Unable to get the application status because the domain UID {{domain}} does not exist.", + "vz-application-status-checker-vz-managed-cluster-not-found-error": "Unable to get the Verrazzano multicluster application status because the Verrazzano managed cluster connectivity configuration for managed cluster {{clusterName}} was not found", + "vz-application-status-checker-check-failed-title": "Verrazzano Application Status Check Failed", + "vz-application-status-checker-check-catch-all-error-message": "Verrazzano application status check failed with an unexpected error: {{error}}.", - "vz-application-status-title": "Application Status for \"{{application}}\"", + "vz-application-status-choose-cluster-dialog-title": "Choose the Verrazzano Cluster to Check Application Status", + "vz-application-status-choose-cluster-name-label": "Verrazzano Cluster Name", + "vz-application-status-choose-cluster-name-help": "Choose the Verrazzano cluster on which to check the status of the deployed application.", + + "vz-application-status-title": "Application Status for \"{{application}}\" in Cluster \"{{clusterName}}\"", "vz-application-status-domain-title": "Domain \"{{domain}}\"", "app-update-title": "Application Update", diff --git a/webui/src/js/utils/k8s-helper.js b/webui/src/js/utils/k8s-helper.js index 77d5401fc..676edb5a0 100644 --- a/webui/src/js/utils/k8s-helper.js +++ b/webui/src/js/utils/k8s-helper.js @@ -13,11 +13,11 @@ function(WktActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, val super(); } - async startVerifyClusterConnectivity(kubeConfig, kubeContext) { - await this.executeAction(this.callVerifyClusterConnectivity, kubeConfig, kubeContext); + async startVerifyClusterConnectivity() { + await this.executeAction(this.callVerifyClusterConnectivity); } - async callVerifyClusterConnectivity(kubeConfig, kubeContext, options) { + async callVerifyClusterConnectivity(options) { if (!options) { options = {}; } @@ -31,6 +31,27 @@ function(WktActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, val return Promise.resolve(false); } + let clusterToCheck; + const availableClusters = this._getTargetClusters(); + if (availableClusters.length > 1) { + const args = [ ]; + availableClusters.forEach(availableCluster => args.push({ + name: availableCluster.name, + label: availableCluster.name + })); + const result = await dialogHelper.promptDialog('k8s-helper-choose-cluster-dialog', + { availableClusters: args }); + if (result?.clusterName) { + clusterToCheck = result.clusterName; + } else { + return Promise.resolve(false); + } + } else if (availableClusters.length === 1) { + clusterToCheck = availableClusters[0].name; + } + + const { targetClusterKubeConfig, targetClusterKubeContext } = this._getTargetCluster(clusterToCheck); + const totalSteps = 4.0; try { let busyDialogMessage = i18n.t('flow-validate-kubectl-exe-in-progress'); @@ -56,8 +77,8 @@ function(WktActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, val busyDialogMessage = i18n.t('flow-kubectl-use-context-in-progress'); dialogHelper.updateBusyDialog(busyDialogMessage, 2/totalSteps); - const kubectlContext = kubeContext || this.project.kubectl.kubeConfigContextToUse.value; - const kubectlOptions = this.getKubectlOptions(kubeConfig); + const kubectlContext = targetClusterKubeContext || this.project.kubectl.kubeConfigContextToUse.value; + const kubectlOptions = this.getKubectlOptions(targetClusterKubeConfig); if (!options.skipKubectlSetContext) { if (! await this.useKubectlContext(kubectlExe, kubectlOptions, kubectlContext, errTitle, errPrefix)) { return Promise.resolve(false); @@ -66,7 +87,7 @@ function(WktActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, val busyDialogMessage = i18n.t('kubectl-helper-verify-connect-in-progress'); dialogHelper.updateBusyDialog(busyDialogMessage, 3/totalSteps); - const verifyConnectivityResult = await this.verifyConnectivity(kubectlExe, kubectlOptions); + const verifyConnectivityResult = await this.verifyConnectivity(kubectlExe, kubectlOptions, clusterToCheck); return Promise.resolve(verifyConnectivityResult); } catch (err) { dialogHelper.closeBusyDialog(); @@ -76,18 +97,20 @@ function(WktActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, val } } - async verifyConnectivity(kubectlExe, kubectlOptions) { + async verifyConnectivity(kubectlExe, kubectlOptions, clusterToCheck) { try { const verifyResult = await window.api.ipc.invoke('kubectl-verify-connection', kubectlExe, kubectlOptions); dialogHelper.closeBusyDialog(); + const clusterName = clusterToCheck || ''; if (verifyResult.isSuccess) { - const title = i18n.t('kubectl-helper-verify-connect-success-title'); + const title = i18n.t('kubectl-helper-verify-connect-success-title', { clusterName }); const message = i18n.t('kubectl-helper-verify-connect-success-message', - {clientVersion: verifyResult.clientVersion, serverVersion: verifyResult.serverVersion}); + { clientVersion: verifyResult.clientVersion, serverVersion: verifyResult.serverVersion, clusterName }); await window.api.ipc.invoke('show-info-message', title, message); } else { - const errTitle = i18n.t('kubectl-helper-verify-connect-failed-title'); - const errMessage = i18n.t('kubectl-helper-verify-connect-failed-error-message', {error: verifyResult.reason}); + const errTitle = i18n.t('kubectl-helper-verify-connect-failed-title', { clusterName }); + const errMessage = i18n.t('kubectl-helper-verify-connect-failed-error-message', + { error: verifyResult.reason, clusterName}); await window.api.ipc.invoke('show-error-message', errTitle, errMessage); return Promise.resolve(false); } @@ -107,6 +130,35 @@ function(WktActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, val return validationObject; } + + _getTargetClusters() { + let clusters = []; + if (this.project.settings.wdtTargetType.value === 'vz') { + clusters.push({ + name: 'local', + kubeConfig: this.project.kubectl.kubeConfig.value, + kubeContext: this.project.kubectl.kubeConfigContextToUse.value + }); + this.project.kubectl.vzManagedClusters.observable() + .forEach(managedClusterData => clusters.push(managedClusterData)); + } + return clusters; + } + + _getTargetCluster(clusterToCheck) { + const result = { + targetClusterKubeConfig: undefined, + targetClusterKubeContext: undefined + }; + if (clusterToCheck) { + const clusterData = this._getTargetClusters().find(cluster => clusterToCheck === cluster.name); + if (clusterData) { + result.targetClusterKubeConfig = clusterData.kubeConfig; + result.targetClusterKubeContext = clusterData.kubeContext; + } + } + return result; + } } return new KubernetesHelper(); diff --git a/webui/src/js/utils/view-helper.js b/webui/src/js/utils/view-helper.js index 6f728418a..6a78951de 100644 --- a/webui/src/js/utils/view-helper.js +++ b/webui/src/js/utils/view-helper.js @@ -12,7 +12,6 @@ define(['ojs/ojcontext'], // width of button column in WKT tables. // ideally this could be specified as 'auto', but oj-table will not set below 100px. this.BUTTON_COLUMN_WIDTH = '55px'; - this.TEXT_BUTTON_COLUMN_WIDTH = '175px'; const thisHelper = this; diff --git a/webui/src/js/utils/vz-application-status-checker.js b/webui/src/js/utils/vz-application-status-checker.js index 71df2edf1..18aead002 100644 --- a/webui/src/js/utils/vz-application-status-checker.js +++ b/webui/src/js/utils/vz-application-status-checker.js @@ -7,7 +7,7 @@ define(['utils/vz-actions-base', 'models/wkt-project', 'models/wkt-console', 'utils/i18n', 'utils/project-io', 'utils/dialog-helper', 'utils/k8s-domain-resource-generator', 'utils/k8s-domain-configmap-generator', - 'utils/validation-helper', 'utils/wkt-logger', 'utils/helm-helper'], + 'utils/validation-helper', 'utils/wkt-logger'], function (VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, K8sDomainResourceGenerator, K8sDomainConfigMapGenerator, validationHelper, wktLogger) { class VerrazzanoApplicationStatusChecker extends VzActionsBase { @@ -29,11 +29,31 @@ function (VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, K8s return Promise.resolve(false); } + let clusterToCheck; + const targetClusters = this._getTargetClusters(); + if (targetClusters.length > 1) { + const args = [ ]; + targetClusters.forEach(targetCluster => args.push({ name:targetCluster, label: targetCluster })); + const result = await dialogHelper.promptDialog('vz-application-status-choose-cluster-dialog', + { targetClusters: args }); + if (result?.clusterName) { + clusterToCheck = result.clusterName; + } else { + return Promise.resolve(false); + } + } else if (targetClusters.length === 1) { + clusterToCheck = targetClusters[0]; + } + + const managedClusterData = this._getTargetCluster(clusterToCheck); + const targetClusterKubeConfig = managedClusterData ? managedClusterData.kubeConfig : undefined; + const targetClusterKubeContext = managedClusterData ? managedClusterData.kubeContext : undefined; + const totalSteps = 8.0; try { const kubectlExe = this.getKubectlExe(); - const kubectlOptions = this.getKubectlOptions(); - const kubectlContext = this.getKubectlContext(); + const kubectlOptions = this.getKubectlOptions(targetClusterKubeConfig); + const kubectlContext = targetClusterKubeContext || this.getKubectlContext(); let operatorMajorVersion = ''; let busyDialogMessage = i18n.t('flow-validate-kubectl-exe-in-progress'); @@ -113,6 +133,7 @@ function (VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, K8s const results = this.buildDomainStatus(domainStatus, operatorMajorVersion); results['domainStatus'] = domainStatus; const options = { + clusterName: clusterToCheck, domainStatus: results.domainStatus, domainOverallStatus: results.domainOverallStatus, applicationName: this.project.vzApplication.applicationName.value, @@ -135,6 +156,10 @@ function (VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, K8s this.project.vzApplication.applicationName.validate(true)); validationObject.addField('vz-application-design-namespace-label', this.project.k8sDomain.kubernetesNamespace.validate(true)); + if (this.project.vzApplication.useMultiClusterApplication.value) { + validationObject.addField('vz-application-design-cluster-names-label', + validationHelper.validateRequiredField(this.project.vzApplication.placementClusters.value)); + } const componentFormConfig = validationObject.getDefaultConfigObject(); componentFormConfig.formName = 'vz-component-design-form-name'; @@ -145,11 +170,52 @@ function (VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, K8s kubectlFormConfig.formName = 'kubectl-title'; validationObject.addField('kubectl-exe-file-path-label', validationHelper.validateRequiredField(this.project.kubectl.executableFilePath.value), kubectlFormConfig); - validationObject.addField('kubectl-helm-exe-file-path-label', - validationHelper.validateRequiredField(this.project.kubectl.helmExecutableFilePath.value), kubectlFormConfig); + + if (this.project.vzApplication.useMultiClusterApplication.value) { + const targetClusters = this.project.vzApplication.placementClusters.value; + if (targetClusters.length > 0) { + for (const targetCluster of targetClusters) { + if (targetCluster !== 'local') { + this._validateVerrazzanoManagedClusterConnectivityEntry(validationObject, kubectlFormConfig, targetCluster); + } + } + } + } return validationObject; } + + _getTargetClusters() { + if (this.project.vzApplication.useMultiClusterApplication.value) { + return this.project.vzApplication.placementClusters.value; + } else { + return ['local']; + } + } + + _getTargetCluster(clusterName) { + if (clusterName !== 'local') { + return this.project.kubectl.vzManagedClusters.observable().find(managedCluster => + managedCluster.name === clusterName); + } + } + + _validateVerrazzanoManagedClusterConnectivityEntry(validationObject, kubectlFormConfig, targetManagedClusterName) { + let found = false; + for (const managedClusterData of this.project.kubectl.vzManagedClusters.observable()) { + if (targetManagedClusterName === managedClusterData.name) { + found = true; + break; + } + } + + if (!found) { + const errorMessage = i18n.t('vz-application-status-checker-vz-managed-cluster-not-found-error', + { clusterName: targetManagedClusterName}); + validationObject.addField('kubectl-vz-managed-cluster-name-heading', + [errorMessage], kubectlFormConfig); + } + } } return new VerrazzanoApplicationStatusChecker(); diff --git a/webui/src/js/viewModels/k8s-helper-choose-cluster-dialog.js b/webui/src/js/viewModels/k8s-helper-choose-cluster-dialog.js new file mode 100644 index 000000000..153ac2888 --- /dev/null +++ b/webui/src/js/viewModels/k8s-helper-choose-cluster-dialog.js @@ -0,0 +1,52 @@ +/** + * @license + * Copyright (c) 2023, Oracle and/or its affiliates. + * Licensed under The Universal Permissive License (UPL), Version 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ +'use strict'; + +define(['accUtils', 'knockout', 'utils/i18n', 'utils/observable-properties', 'utils/validation-helper', + 'ojs/ojarraydataprovider', 'utils/wkt-logger', 'ojs/ojselectcombobox', 'ojs/ojinputtext', 'ojs/ojlabel', + 'ojs/ojbutton', 'ojs/ojdialog', 'ojs/ojformlayout', 'ojs/ojvalidationgroup'], +function(accUtils, ko, i18n, props, validationHelper, ArrayDataProvider) { + function KubernetesHelperChooseClusterDialogModel(args) { + const DIALOG_SELECTOR = '#k8sHelperChooseClusterDialog'; + + this.i18n = i18n; + this.testClusterNames = args.availableClusters; + this.selectedTestClusterName = ko.observable(); + this.testClusterNamesDP = + new ArrayDataProvider(this.testClusterNames, { keyAttributes: 'name' }); + + this.connected = () => { + accUtils.announce('Choose Verrazzano application status choose cluster dialog loaded.', 'assertive'); + // open the dialog after the current thread, which is loading this view model. + // using oj-dialog initial-visibility="show" causes vertical centering issues. + setTimeout(function() { + $(DIALOG_SELECTOR)[0].open(); + }, 1); + }; + + this.labelMapper = (labelId) => { + return i18n.t(`kubectl-helper-choose-cluster-${labelId}`); + }; + + this.okInput = () => { + $(DIALOG_SELECTOR)[0].close(); + + const result = {}; + result.clusterName = this.selectedTestClusterName(); + args.setValue(result); + }; + + this.cancelInput = () => { + $(DIALOG_SELECTOR)[0].close(); + args.setValue(); + }; + } + + /* + * Returns a constructor for the ViewModel. + */ + return KubernetesHelperChooseClusterDialogModel; +}); diff --git a/webui/src/js/viewModels/kubectl-page.js b/webui/src/js/viewModels/kubectl-page.js index ab22da5a3..3cb2dd023 100644 --- a/webui/src/js/viewModels/kubectl-page.js +++ b/webui/src/js/viewModels/kubectl-page.js @@ -54,14 +54,6 @@ function(accUtils, ko, project, i18n, ArrayDataProvider, BufferingDataProvider, headerText: this.labelMapper('vz-managed-cluster-kubecontext-heading'), sortProperty: 'kubeContext' }, - { - 'className': 'wkt-table-delete-cell', - // 'headerClassName': 'wkt-table-add-header', - // 'headerTemplate': 'headerTemplate', - 'template': 'actionTemplate', - 'sortable': 'disable', - width: viewHelper.TEXT_BUTTON_COLUMN_WIDTH - }, { 'className': 'wkt-table-delete-cell', 'headerClassName': 'wkt-table-add-header', @@ -256,13 +248,6 @@ function(accUtils, ko, project, i18n, ArrayDataProvider, BufferingDataProvider, this.project.kubectl.vzManagedClusters.observable.splice(index, 1); }; - this.verifyManagedClusterConnectivity = async (event, context) => { - const index = context.item.index; - const managedClusterData = this.project.kubectl.vzManagedClusters.observable()[index]; - await k8sHelper.startVerifyClusterConnectivity(managedClusterData.kubeConfig, managedClusterData.kubeContext); - await k8sHelper.startVerifyClusterConnectivity(managedClusterData.kubeConfig, managedClusterData.kubeContext); - }; - this.createLink = function (url, label) { return '' + label + ''; }; diff --git a/webui/src/js/viewModels/vz-application-design-view.js b/webui/src/js/viewModels/vz-application-design-view.js index 7a003634a..5789bd1af 100644 --- a/webui/src/js/viewModels/vz-application-design-view.js +++ b/webui/src/js/viewModels/vz-application-design-view.js @@ -309,10 +309,20 @@ function (project, accUtils, utils, ko, i18n, BufferingDataProvider, ArrayDataPr const kubectlExe = this.project.kubectl.executableFilePath.value; const kubectlOptions = k8sHelper.getKubectlOptions(); + const kubectlContext = this.project.kubectl.kubeConfigContextToUse.value; + + let busyDialogMessage = i18n.t('flow-kubectl-use-context-in-progress'); + dialogHelper.openBusyDialog(busyDialogMessage, 'bar', 0 / 2.0); + const useContextResult = + await window.api.ipc.invoke('kubectl-set-current-context', kubectlExe, kubectlContext, kubectlOptions); + if (!useContextResult.isSuccess) { + const errMessage = this.labelMapper('use-context-error-message', { error: useContextResult.reason }); + await window.api.ipc.invoke('show-error-message', errTitle, errMessage); + return Promise.resolve(); + } - const busyDialogMessage = this.labelMapper('get-deployments-in-progress'); - dialogHelper.openBusyDialog(busyDialogMessage, 'bar'); - dialogHelper.openBusyDialog(busyDialogMessage, 0); + busyDialogMessage = this.labelMapper('get-deployments-in-progress'); + dialogHelper.openBusyDialog(busyDialogMessage, 'bar', 1 / 2.0); const deploymentNamesResult = await window.api.ipc.invoke('get-verrazzano-deployment-names-all-namespaces', kubectlExe, kubectlOptions); dialogHelper.closeBusyDialog(); @@ -351,10 +361,21 @@ function (project, accUtils, utils, ko, i18n, BufferingDataProvider, ArrayDataPr const kubectlExe = this.project.kubectl.executableFilePath.value; const kubectlOptions = k8sHelper.getKubectlOptions(); + const kubectlContext = this.project.kubectl.kubeConfigContextToUse.value; + + let busyDialogMessage = i18n.t('flow-kubectl-use-context-in-progress'); + dialogHelper.openBusyDialog(busyDialogMessage, 'bar', 0 / 2.0); + const useContextResult = + await window.api.ipc.invoke('kubectl-set-current-context', kubectlExe, kubectlContext, kubectlOptions); + if (!useContextResult.isSuccess) { + const errMessage = this.labelMapper('use-context-error-message', { error: useContextResult.reason }); + await window.api.ipc.invoke('show-error-message', errTitle, errMessage); + return Promise.resolve(); + } + + busyDialogMessage = this.labelMapper('get-clusters-in-progress'); + dialogHelper.updateBusyDialog(busyDialogMessage, 1 / 2.0); - const busyDialogMessage = this.labelMapper('get-clusters-in-progress'); - dialogHelper.openBusyDialog(busyDialogMessage, 'bar'); - dialogHelper.openBusyDialog(busyDialogMessage, 0); const clusterNamesResult = await window.api.ipc.invoke('get-verrazzano-cluster-names', kubectlExe, kubectlOptions); dialogHelper.closeBusyDialog(); diff --git a/webui/src/js/viewModels/vz-application-status-choose-cluster-dialog.js b/webui/src/js/viewModels/vz-application-status-choose-cluster-dialog.js new file mode 100644 index 000000000..5552ba4d2 --- /dev/null +++ b/webui/src/js/viewModels/vz-application-status-choose-cluster-dialog.js @@ -0,0 +1,52 @@ +/** + * @license + * Copyright (c) 2023, Oracle and/or its affiliates. + * Licensed under The Universal Permissive License (UPL), Version 1.0 as shown at https://oss.oracle.com/licenses/upl/ + */ +'use strict'; + +define(['accUtils', 'knockout', 'utils/i18n', 'utils/observable-properties', 'utils/validation-helper', + 'ojs/ojarraydataprovider', 'utils/wkt-logger', 'ojs/ojselectcombobox', 'ojs/ojinputtext', 'ojs/ojlabel', + 'ojs/ojbutton', 'ojs/ojdialog', 'ojs/ojformlayout', 'ojs/ojvalidationgroup'], +function(accUtils, ko, i18n, props, validationHelper, ArrayDataProvider) { + function VerrazzanoApplicationStatusChooseClusterDialogModel(args) { + const DIALOG_SELECTOR = '#verrazzanoApplicationStatusChooseClusterDialog'; + + this.i18n = i18n; + this.targetClusterNames = args.targetClusters; + this.selectedTargetClusterName = ko.observable(); + this.targetClusterNamesDP = + new ArrayDataProvider(this.targetClusterNames, { keyAttributes: 'name' }); + + this.connected = () => { + accUtils.announce('Choose Verrazzano application status choose cluster dialog loaded.', 'assertive'); + // open the dialog after the current thread, which is loading this view model. + // using oj-dialog initial-visibility="show" causes vertical centering issues. + setTimeout(function() { + $(DIALOG_SELECTOR)[0].open(); + }, 1); + }; + + this.labelMapper = (labelId) => { + return i18n.t(`vz-application-status-choose-cluster-${labelId}`); + }; + + this.okInput = () => { + $(DIALOG_SELECTOR)[0].close(); + + const result = {}; + result.clusterName = this.selectedTargetClusterName(); + args.setValue(result); + }; + + this.cancelInput = () => { + $(DIALOG_SELECTOR)[0].close(); + args.setValue(); + }; + } + + /* + * Returns a constructor for the ViewModel. + */ + return VerrazzanoApplicationStatusChooseClusterDialogModel; +}); diff --git a/webui/src/js/viewModels/vz-application-status-dialog.js b/webui/src/js/viewModels/vz-application-status-dialog.js index 23b2bb965..162274b02 100644 --- a/webui/src/js/viewModels/vz-application-status-dialog.js +++ b/webui/src/js/viewModels/vz-application-status-dialog.js @@ -12,10 +12,12 @@ function(accUtils, ko, jsyaml, i18n, project, viewHelper) { const DIALOG_SELECTOR = '#applicationStatusDialog'; this.project = project; + this.clusterName = args.clusterName; this.domainStatus = args.domainStatus; this.domainOverallStatus = args.domainOverallStatus; - this.title = i18n.t('vz-application-status-title', { application: args.applicationName }); + this.title = i18n.t('vz-application-status-title', + { clusterName: this.clusterName, application: args.applicationName }); this.dialogTitle = i18n.t('vz-application-status-domain-title', { domain: args.domainName }); this.domainMessage = this.domainStatus.message; diff --git a/webui/src/js/viewModels/vz-install-design-view.js b/webui/src/js/viewModels/vz-install-design-view.js index 46fc8f15b..907d090d2 100644 --- a/webui/src/js/viewModels/vz-install-design-view.js +++ b/webui/src/js/viewModels/vz-install-design-view.js @@ -20,7 +20,7 @@ function (project, accUtils, utils, ko, i18n, screenUtils, BufferingDataProvider this.connected = () => { accUtils.announce('Verrazzano Install Design View page loaded.', 'assertive'); - subscriptions.push(this.project.vzInstall.actualInstalledVersion.observable.subscribe(newTagValue => { + subscriptions.push(this.project.vzInstall.versionTag.observable.subscribe(newTagValue => { this.computedArgoCDAvailabilityFromVersion(newTagValue); })); diff --git a/webui/src/js/views/choose-kubectl-context-dialog.html b/webui/src/js/views/choose-kubectl-context-dialog.html index 9e6d7abad..7bd04d796 100644 --- a/webui/src/js/views/choose-kubectl-context-dialog.html +++ b/webui/src/js/views/choose-kubectl-context-dialog.html @@ -13,7 +13,7 @@ diff --git a/webui/src/js/views/k8s-helper-choose-cluster-dialog.html b/webui/src/js/views/k8s-helper-choose-cluster-dialog.html new file mode 100644 index 000000000..ec5594398 --- /dev/null +++ b/webui/src/js/views/k8s-helper-choose-cluster-dialog.html @@ -0,0 +1,31 @@ + + +
+ + + +
+
+
+ + + + +
+
+ +
+ + + + + + +
+
diff --git a/webui/src/js/views/kubectl-page.html b/webui/src/js/views/kubectl-page.html index 2ec6f72d7..1f9a0cc02 100644 --- a/webui/src/js/views/kubectl-page.html +++ b/webui/src/js/views/kubectl-page.html @@ -5,6 +5,15 @@
+ + + + +
@@ -56,18 +65,7 @@
-
-
- - - - - -
+
-
-
-
-
- - + +
diff --git a/webui/src/js/views/vz-application-status-choose-cluster-dialog.html b/webui/src/js/views/vz-application-status-choose-cluster-dialog.html new file mode 100644 index 000000000..2857fdb92 --- /dev/null +++ b/webui/src/js/views/vz-application-status-choose-cluster-dialog.html @@ -0,0 +1,31 @@ + + +
+ + + +
+
+
+ + + + +
+
+ +
+ + + + + + +
+
diff --git a/webui/src/js/windowStateUtils.js b/webui/src/js/windowStateUtils.js index 992e8b6e2..37371f511 100644 --- a/webui/src/js/windowStateUtils.js +++ b/webui/src/js/windowStateUtils.js @@ -11,7 +11,7 @@ define(['models/wkt-project', 'models/wkt-console', 'utils/wdt-discoverer', 'uti 'utils/k8s-domain-undeployer', 'utils/ingress-controller-installer', 'utils/ingress-routes-updater', 'utils/ingress-controller-uninstaller', 'utils/vz-installer', 'utils/vz-install-status-checker', 'utils/vz-component-deployer', 'utils/vz-component-undeployer', 'utils/vz-application-deployer', - 'utils/vz-application-undeployer','utils/app-updater', 'utils/wkt-logger'], + 'utils/vz-application-status-checker', 'utils/vz-application-undeployer','utils/app-updater', 'utils/wkt-logger'], function(wktProject, wktConsole, wdtDiscoverer, dialogHelper, projectIO, utils, wdtModelPreparer, wdtModelValidator, i18n, witImageCreator, witAuxImageCreator, imagePusher, auxImagePusher, k8sHelper, @@ -19,7 +19,7 @@ function(wktProject, wktConsole, wdtDiscoverer, dialogHelper, projectIO, k8sDomainStatusChecker, k8sDomainUndeployer, ingressControllerInstaller, ingressRoutesUpdater, ingressControllerUninstaller, vzInstaller, vzInstallStatusChecker, vzComponentDeployer, vzComponentUndeployer, - vzApplicationDeployer, vzApplicationUndeployer, appUpdater, wktLogger) { + vzApplicationDeployer, vzApplicationStatusChecker, vzApplicationUndeployer, appUpdater, wktLogger) { async function displayCatchAllError(i18nPrefix, err) { return dialogHelper.displayCatchAllError(i18nPrefix, err); @@ -279,6 +279,13 @@ function(wktProject, wktConsole, wdtDiscoverer, dialogHelper, projectIO, }); }); + window.api.ipc.receive('start-get-vz-application-status', async () => { + blurSelection(); + vzApplicationStatusChecker.startCheckApplicationStatus().then(() => Promise.resolve()).catch(err => { + displayCatchAllError('vz-application-status-checker-check', err).then(() => Promise.resolve()); + }); + }); + window.api.ipc.receive('start-undeploy-verrazzano-application', async () => { blurSelection(); vzApplicationUndeployer.startUndeployApplication().then(() => Promise.resolve()).catch(err => {