Skip to content

fixing Verrazzano App Status Checks to work across clusters #210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions electron/app/js/ipcRendererPreload.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
17 changes: 16 additions & 1 deletion electron/app/js/wktWindow.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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'),
Expand Down
3 changes: 3 additions & 0 deletions electron/app/locales/en/electron.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.",
Expand Down
21 changes: 16 additions & 5 deletions electron/app/locales/en/webui.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
74 changes: 63 additions & 11 deletions webui/src/js/utils/k8s-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {};
}
Expand All @@ -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');
Expand All @@ -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);
Expand All @@ -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();
Expand All @@ -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);
}
Expand All @@ -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();
Expand Down
1 change: 0 additions & 1 deletion webui/src/js/utils/view-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
76 changes: 71 additions & 5 deletions webui/src/js/utils/vz-application-status-checker.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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');
Expand Down Expand Up @@ -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,
Expand All @@ -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';
Expand All @@ -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();
Expand Down
Loading