Skip to content

Commit 81c72c9

Browse files
fixing Verrazzano App Status Checks to work across clusters (#210)
1 parent b0ec88b commit 81c72c9

18 files changed

+445
-134
lines changed

electron/app/js/ipcRendererPreload.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ contextBridge.exposeInMainWorld(
9292
'start-verrazzano-install',
9393
'start-get-verrazzano-install-status',
9494
'start-deploy-verrazzano-component',
95+
'start-get-vz-application-status',
9596
'start-undeploy-verrazzano-component',
9697
'start-deploy-verrazzano-application',
9798
'start-undeploy-verrazzano-application',

electron/app/js/wktWindow.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ class WktAppMenu {
408408
if (!focusedWindow) {
409409
return dialog.showErrorBox(
410410
i18n.t('menu-go-domain-status-title'),
411-
i18n.t('menu-go-domain=status-error-message')
411+
i18n.t('menu-go-domain-status-error-message')
412412
);
413413
}
414414
sendToWindow(focusedWindow, 'start-get-k8s-domain-status');
@@ -550,6 +550,21 @@ class WktAppMenu {
550550
sendToWindow(focusedWindow,'start-deploy-verrazzano-application');
551551
}
552552
},
553+
{
554+
id: 'checkVerrazzanoApplicationStatus',
555+
label: i18n.t('menu-go-check-verrazzano-application-status'),
556+
visible: !this._isWkoTarget,
557+
enabled: !this._hasOpenDialog,
558+
click(item, focusedWindow) {
559+
if (!focusedWindow) {
560+
return dialog.showErrorBox(
561+
i18n.t('menu-go-check-verrazzano-application-status-error-title'),
562+
i18n.t('menu-go-check-verrazzano-application-status-error-message')
563+
);
564+
}
565+
sendToWindow(focusedWindow, 'start-get-vz-application-status');
566+
}
567+
},
553568
{
554569
id: 'undeployVerrazzanoApplication',
555570
label: i18n.t('menu-go-undeploy-verrazzano-application'),

electron/app/locales/en/electron.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@
9393
"menu-go-deploy-verrazzano-application": "Deploy Verrazzano Application",
9494
"menu-go-deploy-verrazzano-application-error-title": "No Active Project Window",
9595
"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.",
96+
"menu-go-check-verrazzano-application-status": "Check Verrazzano Application Status",
97+
"menu-go-check-verrazzano-application-status-error-title": "No Active Project Window",
98+
"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.",
9699
"menu-go-undeploy-verrazzano-application": "Undeploy Verrazzano Application",
97100
"menu-go-undeploy-verrazzano-application-error-title": "No Active Project Window",
98101
"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.",

electron/app/locales/en/webui.json

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,14 @@
102102
"kubectl-helper-project-not-saved-error-prefix": "Unable to verify Kubernetes client connectivity because project save failed",
103103
"kubectl-helper-set-context-error-message": "Unable to verify Kubernetes client connectivity because setting the Kubernetes client cluster context failed: {{error}}.",
104104
"kubectl-helper-verify-connect-in-progress": "Verifying Kubernetes Client Connectivity",
105-
"kubectl-helper-verify-connect-success-title": "Verify Kubernetes Client Connectivity Success",
106-
"kubectl-helper-verify-connect-success-message": "Kubernetes client (version {{clientVersion}}) successfully connected to the Kubernetes cluster (version {{serverVersion}}).",
107-
"kubectl-helper-verify-connect-failed-title": "Verify Kubernetes Client Connectivity Failed",
108-
"kubectl-helper-verify-connect-failed-error-message": "Failed to verify Kubernetes client connectivity: {{error}}.",
105+
"kubectl-helper-verify-connect-success-title": "Verify Kubernetes Client Connectivity Success to Cluster {{clusterName}}",
106+
"kubectl-helper-verify-connect-success-message": "Kubernetes client (version {{clientVersion}}) successfully connected to the Kubernetes cluster {{clusterName}} (version {{serverVersion}}).",
107+
"kubectl-helper-verify-connect-failed-title": "Verify Kubernetes Client Connectivity Failed for Cluster {{clusterName}}",
108+
"kubectl-helper-verify-connect-failed-error-message": "Failed to verify Kubernetes client connectivity to cluster {{clusterName}}: {{error}}.",
109109
"kubectl-helper-verify-connect-catch-all-error-message": "Failed to verify Kubernetes client connectivity due to an unexpected error: {{error}}.",
110+
"kubectl-helper-choose-cluster-dialog-title": "Choose the Verrazzano cluster to verify connectivity",
111+
"kubectl-helper-choose-cluster-name-label": "Verrazzano Cluster Name",
112+
"kubectl-helper-choose-cluster-name-help": "The Verrazzano cluster name for which to verify connectivity.",
110113

111114
"project-settings-form-name": "Project Settings",
112115
"project-settings-title": "Project Settings",
@@ -1642,6 +1645,7 @@
16421645
"vz-application-design-choose-clusters-flow-nane": "Choose Verrazzano Clusters",
16431646
"vz-application-design-choose-clusters-validation-error-title": "Choose Verrazzano Clusters for Application Placement Aborted",
16441647
"vz-application-design-get-clusters-in-progress": "Getting Verrazzano Clusters",
1648+
"vz-application-design-use-context-error-message": "Unable to choose clusters due to an error while switching the Kubernetes cluster context: {{error}}.",
16451649
"vz-application-design-get-clusters-error-message": "Unable to choose clusters due to an error while getting the list of available clusters: {{error}}.",
16461650
"vz-application-design-choose-clusters-dialog-title": "Choose Verrazzano Clusters for Application Placement",
16471651
"vz-application-design-choose-clusters-name-label": "Verrazzano Cluster Names",
@@ -1772,8 +1776,15 @@
17721776
"vz-application-status-checker-application-not-exist-error-message": "Unable to get the application status because the application {{application}} does not exist.",
17731777
"vz-application-status-checker-ns-not-exist-error-message": "Unable to get the application status because the application namespace {{namespace}} does not exist.",
17741778
"vz-application-status-checker-domain-not-exist-error-message": "Unable to get the application status because the domain UID {{domain}} does not exist.",
1779+
"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",
1780+
"vz-application-status-checker-check-failed-title": "Verrazzano Application Status Check Failed",
1781+
"vz-application-status-checker-check-catch-all-error-message": "Verrazzano application status check failed with an unexpected error: {{error}}.",
17751782

1776-
"vz-application-status-title": "Application Status for \"{{application}}\"",
1783+
"vz-application-status-choose-cluster-dialog-title": "Choose the Verrazzano Cluster to Check Application Status",
1784+
"vz-application-status-choose-cluster-name-label": "Verrazzano Cluster Name",
1785+
"vz-application-status-choose-cluster-name-help": "Choose the Verrazzano cluster on which to check the status of the deployed application.",
1786+
1787+
"vz-application-status-title": "Application Status for \"{{application}}\" in Cluster \"{{clusterName}}\"",
17771788
"vz-application-status-domain-title": "Domain \"{{domain}}\"",
17781789

17791790
"app-update-title": "Application Update",

webui/src/js/utils/k8s-helper.js

Lines changed: 63 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ function(WktActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, val
1313
super();
1414
}
1515

16-
async startVerifyClusterConnectivity(kubeConfig, kubeContext) {
17-
await this.executeAction(this.callVerifyClusterConnectivity, kubeConfig, kubeContext);
16+
async startVerifyClusterConnectivity() {
17+
await this.executeAction(this.callVerifyClusterConnectivity);
1818
}
1919

20-
async callVerifyClusterConnectivity(kubeConfig, kubeContext, options) {
20+
async callVerifyClusterConnectivity(options) {
2121
if (!options) {
2222
options = {};
2323
}
@@ -31,6 +31,27 @@ function(WktActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, val
3131
return Promise.resolve(false);
3232
}
3333

34+
let clusterToCheck;
35+
const availableClusters = this._getTargetClusters();
36+
if (availableClusters.length > 1) {
37+
const args = [ ];
38+
availableClusters.forEach(availableCluster => args.push({
39+
name: availableCluster.name,
40+
label: availableCluster.name
41+
}));
42+
const result = await dialogHelper.promptDialog('k8s-helper-choose-cluster-dialog',
43+
{ availableClusters: args });
44+
if (result?.clusterName) {
45+
clusterToCheck = result.clusterName;
46+
} else {
47+
return Promise.resolve(false);
48+
}
49+
} else if (availableClusters.length === 1) {
50+
clusterToCheck = availableClusters[0].name;
51+
}
52+
53+
const { targetClusterKubeConfig, targetClusterKubeContext } = this._getTargetCluster(clusterToCheck);
54+
3455
const totalSteps = 4.0;
3556
try {
3657
let busyDialogMessage = i18n.t('flow-validate-kubectl-exe-in-progress');
@@ -56,8 +77,8 @@ function(WktActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, val
5677

5778
busyDialogMessage = i18n.t('flow-kubectl-use-context-in-progress');
5879
dialogHelper.updateBusyDialog(busyDialogMessage, 2/totalSteps);
59-
const kubectlContext = kubeContext || this.project.kubectl.kubeConfigContextToUse.value;
60-
const kubectlOptions = this.getKubectlOptions(kubeConfig);
80+
const kubectlContext = targetClusterKubeContext || this.project.kubectl.kubeConfigContextToUse.value;
81+
const kubectlOptions = this.getKubectlOptions(targetClusterKubeConfig);
6182
if (!options.skipKubectlSetContext) {
6283
if (! await this.useKubectlContext(kubectlExe, kubectlOptions, kubectlContext, errTitle, errPrefix)) {
6384
return Promise.resolve(false);
@@ -66,7 +87,7 @@ function(WktActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, val
6687

6788
busyDialogMessage = i18n.t('kubectl-helper-verify-connect-in-progress');
6889
dialogHelper.updateBusyDialog(busyDialogMessage, 3/totalSteps);
69-
const verifyConnectivityResult = await this.verifyConnectivity(kubectlExe, kubectlOptions);
90+
const verifyConnectivityResult = await this.verifyConnectivity(kubectlExe, kubectlOptions, clusterToCheck);
7091
return Promise.resolve(verifyConnectivityResult);
7192
} catch (err) {
7293
dialogHelper.closeBusyDialog();
@@ -76,18 +97,20 @@ function(WktActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, val
7697
}
7798
}
7899

79-
async verifyConnectivity(kubectlExe, kubectlOptions) {
100+
async verifyConnectivity(kubectlExe, kubectlOptions, clusterToCheck) {
80101
try {
81102
const verifyResult = await window.api.ipc.invoke('kubectl-verify-connection', kubectlExe, kubectlOptions);
82103
dialogHelper.closeBusyDialog();
104+
const clusterName = clusterToCheck || '';
83105
if (verifyResult.isSuccess) {
84-
const title = i18n.t('kubectl-helper-verify-connect-success-title');
106+
const title = i18n.t('kubectl-helper-verify-connect-success-title', { clusterName });
85107
const message = i18n.t('kubectl-helper-verify-connect-success-message',
86-
{clientVersion: verifyResult.clientVersion, serverVersion: verifyResult.serverVersion});
108+
{ clientVersion: verifyResult.clientVersion, serverVersion: verifyResult.serverVersion, clusterName });
87109
await window.api.ipc.invoke('show-info-message', title, message);
88110
} else {
89-
const errTitle = i18n.t('kubectl-helper-verify-connect-failed-title');
90-
const errMessage = i18n.t('kubectl-helper-verify-connect-failed-error-message', {error: verifyResult.reason});
111+
const errTitle = i18n.t('kubectl-helper-verify-connect-failed-title', { clusterName });
112+
const errMessage = i18n.t('kubectl-helper-verify-connect-failed-error-message',
113+
{ error: verifyResult.reason, clusterName});
91114
await window.api.ipc.invoke('show-error-message', errTitle, errMessage);
92115
return Promise.resolve(false);
93116
}
@@ -107,6 +130,35 @@ function(WktActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, val
107130

108131
return validationObject;
109132
}
133+
134+
_getTargetClusters() {
135+
let clusters = [];
136+
if (this.project.settings.wdtTargetType.value === 'vz') {
137+
clusters.push({
138+
name: 'local',
139+
kubeConfig: this.project.kubectl.kubeConfig.value,
140+
kubeContext: this.project.kubectl.kubeConfigContextToUse.value
141+
});
142+
this.project.kubectl.vzManagedClusters.observable()
143+
.forEach(managedClusterData => clusters.push(managedClusterData));
144+
}
145+
return clusters;
146+
}
147+
148+
_getTargetCluster(clusterToCheck) {
149+
const result = {
150+
targetClusterKubeConfig: undefined,
151+
targetClusterKubeContext: undefined
152+
};
153+
if (clusterToCheck) {
154+
const clusterData = this._getTargetClusters().find(cluster => clusterToCheck === cluster.name);
155+
if (clusterData) {
156+
result.targetClusterKubeConfig = clusterData.kubeConfig;
157+
result.targetClusterKubeContext = clusterData.kubeContext;
158+
}
159+
}
160+
return result;
161+
}
110162
}
111163

112164
return new KubernetesHelper();

webui/src/js/utils/view-helper.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ define(['ojs/ojcontext'],
1212
// width of button column in WKT tables.
1313
// ideally this could be specified as 'auto', but oj-table will not set below 100px.
1414
this.BUTTON_COLUMN_WIDTH = '55px';
15-
this.TEXT_BUTTON_COLUMN_WIDTH = '175px';
1615

1716
const thisHelper = this;
1817

webui/src/js/utils/vz-application-status-checker.js

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
define(['utils/vz-actions-base', 'models/wkt-project', 'models/wkt-console', 'utils/i18n', 'utils/project-io',
99
'utils/dialog-helper', 'utils/k8s-domain-resource-generator', 'utils/k8s-domain-configmap-generator',
10-
'utils/validation-helper', 'utils/wkt-logger', 'utils/helm-helper'],
10+
'utils/validation-helper', 'utils/wkt-logger'],
1111
function (VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, K8sDomainResourceGenerator,
1212
K8sDomainConfigMapGenerator, validationHelper, wktLogger) {
1313
class VerrazzanoApplicationStatusChecker extends VzActionsBase {
@@ -29,11 +29,31 @@ function (VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, K8s
2929
return Promise.resolve(false);
3030
}
3131

32+
let clusterToCheck;
33+
const targetClusters = this._getTargetClusters();
34+
if (targetClusters.length > 1) {
35+
const args = [ ];
36+
targetClusters.forEach(targetCluster => args.push({ name:targetCluster, label: targetCluster }));
37+
const result = await dialogHelper.promptDialog('vz-application-status-choose-cluster-dialog',
38+
{ targetClusters: args });
39+
if (result?.clusterName) {
40+
clusterToCheck = result.clusterName;
41+
} else {
42+
return Promise.resolve(false);
43+
}
44+
} else if (targetClusters.length === 1) {
45+
clusterToCheck = targetClusters[0];
46+
}
47+
48+
const managedClusterData = this._getTargetCluster(clusterToCheck);
49+
const targetClusterKubeConfig = managedClusterData ? managedClusterData.kubeConfig : undefined;
50+
const targetClusterKubeContext = managedClusterData ? managedClusterData.kubeContext : undefined;
51+
3252
const totalSteps = 8.0;
3353
try {
3454
const kubectlExe = this.getKubectlExe();
35-
const kubectlOptions = this.getKubectlOptions();
36-
const kubectlContext = this.getKubectlContext();
55+
const kubectlOptions = this.getKubectlOptions(targetClusterKubeConfig);
56+
const kubectlContext = targetClusterKubeContext || this.getKubectlContext();
3757
let operatorMajorVersion = '';
3858

3959
let busyDialogMessage = i18n.t('flow-validate-kubectl-exe-in-progress');
@@ -113,6 +133,7 @@ function (VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, K8s
113133
const results = this.buildDomainStatus(domainStatus, operatorMajorVersion);
114134
results['domainStatus'] = domainStatus;
115135
const options = {
136+
clusterName: clusterToCheck,
116137
domainStatus: results.domainStatus,
117138
domainOverallStatus: results.domainOverallStatus,
118139
applicationName: this.project.vzApplication.applicationName.value,
@@ -135,6 +156,10 @@ function (VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, K8s
135156
this.project.vzApplication.applicationName.validate(true));
136157
validationObject.addField('vz-application-design-namespace-label',
137158
this.project.k8sDomain.kubernetesNamespace.validate(true));
159+
if (this.project.vzApplication.useMultiClusterApplication.value) {
160+
validationObject.addField('vz-application-design-cluster-names-label',
161+
validationHelper.validateRequiredField(this.project.vzApplication.placementClusters.value));
162+
}
138163

139164
const componentFormConfig = validationObject.getDefaultConfigObject();
140165
componentFormConfig.formName = 'vz-component-design-form-name';
@@ -145,11 +170,52 @@ function (VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, K8s
145170
kubectlFormConfig.formName = 'kubectl-title';
146171
validationObject.addField('kubectl-exe-file-path-label',
147172
validationHelper.validateRequiredField(this.project.kubectl.executableFilePath.value), kubectlFormConfig);
148-
validationObject.addField('kubectl-helm-exe-file-path-label',
149-
validationHelper.validateRequiredField(this.project.kubectl.helmExecutableFilePath.value), kubectlFormConfig);
173+
174+
if (this.project.vzApplication.useMultiClusterApplication.value) {
175+
const targetClusters = this.project.vzApplication.placementClusters.value;
176+
if (targetClusters.length > 0) {
177+
for (const targetCluster of targetClusters) {
178+
if (targetCluster !== 'local') {
179+
this._validateVerrazzanoManagedClusterConnectivityEntry(validationObject, kubectlFormConfig, targetCluster);
180+
}
181+
}
182+
}
183+
}
150184

151185
return validationObject;
152186
}
187+
188+
_getTargetClusters() {
189+
if (this.project.vzApplication.useMultiClusterApplication.value) {
190+
return this.project.vzApplication.placementClusters.value;
191+
} else {
192+
return ['local'];
193+
}
194+
}
195+
196+
_getTargetCluster(clusterName) {
197+
if (clusterName !== 'local') {
198+
return this.project.kubectl.vzManagedClusters.observable().find(managedCluster =>
199+
managedCluster.name === clusterName);
200+
}
201+
}
202+
203+
_validateVerrazzanoManagedClusterConnectivityEntry(validationObject, kubectlFormConfig, targetManagedClusterName) {
204+
let found = false;
205+
for (const managedClusterData of this.project.kubectl.vzManagedClusters.observable()) {
206+
if (targetManagedClusterName === managedClusterData.name) {
207+
found = true;
208+
break;
209+
}
210+
}
211+
212+
if (!found) {
213+
const errorMessage = i18n.t('vz-application-status-checker-vz-managed-cluster-not-found-error',
214+
{ clusterName: targetManagedClusterName});
215+
validationObject.addField('kubectl-vz-managed-cluster-name-heading',
216+
[errorMessage], kubectlFormConfig);
217+
}
218+
}
153219
}
154220

155221
return new VerrazzanoApplicationStatusChecker();

0 commit comments

Comments
 (0)