Skip to content

fixing Verrazzano app/component deployment issues #187

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 2 commits into from
Nov 2, 2022
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 @@ -232,6 +232,7 @@ contextBridge.exposeInMainWorld(
'get-verrazzano-secret-names',
'get-verrazzano-cluster-names',
'get-verrazzano-deployment-names-all-namespaces',
'verify-verrazzano-components-exist',
'deploy-verrazzano-project',
'deploy-verrazzano-application',
'undeploy-verrazzano-application',
Expand Down
40 changes: 38 additions & 2 deletions electron/app/js/kubectlUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -688,7 +688,7 @@ async function apply(kubectlExe, fileData, options) {
fsUtils.writeTempFile(fileData, { baseName: 'k8sApplyData', extension: '.yaml' }).then(fileName => {
const args = [ 'apply', '-f', fileName ];
executeFileCommand(kubectlExe, args, env).then(stdout => {
console.log('kubectl apply returned: %s', stdout);
getLogger().debug('kubectl apply returned: %s', stdout);
fsUtils.recursivelyRemoveTemporaryFileDirectory(fileName).then(() => resolve(result)).catch(err => {
getLogger().warn('kubectlUtils.apply() failed to remove temporary file %s: %s', fileName, err);
resolve(result);
Expand Down Expand Up @@ -860,7 +860,12 @@ async function isVerrazzanoInstalled(kubectlExe, options) {
const vzObject = vzObjectList[0];
result.isInstalled = true;
result.name = vzObject.metadata?.name;
result.version = vzObject.status?.version;

let statusVersion = vzObject.status?.version;
if (statusVersion && statusVersion.startsWith('v')) {
statusVersion = statusVersion.slice(1);
}
result.version = statusVersion;
}
resolve(result);
}).catch(err => {
Expand Down Expand Up @@ -972,6 +977,36 @@ async function getKubernetesObjectsFromAllNamespaces(kubectlExe, kubectlOptions,
});
}

async function verifyVerrazzanoComponentsDeployed(kubectlExe, componentNames, namespace, kubectlOptions) {
const httpsProxyUrl = getHttpsProxyUrl();
const bypassProxyHosts = getBypassProxyHosts();

const env = getKubectlEnvironment(kubectlOptions, httpsProxyUrl, bypassProxyHosts);

const result = {
isSuccess: true
};

const missingComponentNames = [];
for (const componentName of componentNames) {
const args = [ 'get', 'Component', componentName, '-n', namespace ];
try {
const stdout = await executeFileCommand(kubectlExe, args, env);
getLogger().debug('Found component %s in namespace %s: %s', componentName, namespace, stdout);
} catch (err) {
getLogger().warn('Error getting component %s in namespace %s: %s', componentName, namespace, getErrorMessage(err));
missingComponentNames.push(componentName);
}
}

if (missingComponentNames.length > 0) {
result.isSuccess = false;
result.reason = i18n.t('kubectl-verify-vz-components-deployed-error-message',
{ namespace: namespace, missingComponents: missingComponentNames.join(', ')});
}
return Promise.resolve(result);
}

async function doCreateSecret(kubectlExe, createArgs, env, namespace, secret, resolve, results, key) {
executeFileCommand(kubectlExe, createArgs, env, true).then(() => {
resolve(results);
Expand Down Expand Up @@ -1063,5 +1098,6 @@ module.exports = {
validateDomainExist,
validateApplicationExist,
verifyClusterConnectivity,
verifyVerrazzanoComponentsDeployed,
verifyVerrazzanoPlatformOperatorRollout,
};
1 change: 1 addition & 0 deletions electron/app/locales/en/electron.json
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@
"kubectl-get-named-vz-install-error-message": "Unable to get Verrazzano installation with name {{name}}: {{error}}",
"kubectl-get-vz-application-status-error-message": "Unable to get Verrazzano application status: {{error}}",
"kubectl-get-operator-version-not-found-error-message": "Failed to find the operator version from its log entries",
"kubectl-verify-vz-components-deployed-error-message": "Unable to find one or more components in namespace {{namespace}}: {{missingComponents}}",

"helm-not-specified-error-message": "Helm executable path was not provided",
"helm-not-exists-error-message": "Helm executable {{filePath}} does not exist",
Expand Down
7 changes: 7 additions & 0 deletions electron/app/locales/en/webui.json
Original file line number Diff line number Diff line change
Expand Up @@ -1324,6 +1324,7 @@
"flow-getting-vz-application-status-in-progress": "Getting Application Status",
"flow-checking-operator-version-in-progress": "Checking operator version",
"flow-checking-vz-already-installed-in-progress": "Checking if Verrazzano is already installed.",
"flow-checking-vz-app-components-deployed-in-progress": "Checking if Verrazzano application's components are deployed.",
"flow-install-verrazzano-name": "Install Verrazzano",
"flow-verrazzano-install-status-check-name": "Check Verrazzano Install Status",
"flow-verrazzano-get-install-version-name": "Get Verrazzano Installed Version",
Expand Down Expand Up @@ -1564,8 +1565,11 @@
"vz-application-design-project-name-label": "Verrazzano Project Name",
"vz-application-design-project-name-help": "The name to use when creating the new Verrazzano project for this multicluster application.",
"vz-application-design-components-title": "Components",
"vz-application-design-components-label": "Components",
"vz-application-design-component-name-label": "Component Name",
"vz-application-design-component-name-help": "The name of the Verrazzano component used by this application.",
"vz-application-design-delete-component-button-label": "Delete Component",
"vz-application-design-delete-component-button-help": "Delete the component reference from the application.",
"vz-application-design-add-component-button-label": "Add Component",
"vz-application.design-add-component-flow-nane": "Add Component",
"vz-application-design-add-component-validation-error-title": "Add Component to Application Aborted",
Expand Down Expand Up @@ -1662,11 +1666,13 @@
"vz-application-design-ingress-trait-rule-edit-paths-table-aria-label": "Paths table",
"vz-application-design-ingress-trait-rule-edit-path-label": "Path",
"vz-application-design-ingress-trait-rule-edit-path-help": "The path value to match using the specified Path Type value.",
"vz-application-design-ingress-trait-rule-edit-path-placeholder": "Enter Path Value",
"vz-application-design-ingress-trait-rule-edit-path-type-label": "Path Type",
"vz-application-design-ingress-trait-rule-edit-path-type-prefix-label": "Prefix",
"vz-application-design-ingress-trait-rule-edit-path-type-exact-label": "Exact",
"vz-application-design-ingress-trait-rule-edit-path-type-regex-label": "Regular Expression",
"vz-application-design-ingress-trait-rule-edit-path-type-help": "The expression type to use to process the Path value.",
"vz-application-design-ingress-trait-rule-edit-path-type-placeholder": "Select Path Type",
"vz-application-design-ingress-trait-rule-edit-path-add-row-tooltip": "Add Path",
"vz-application-design-ingress-trait-rule-edit-path-delete-row-tooltip": "Delete Path",
"vz-application-design-ingress-trait-rule-editASdd-destination-title": "Destination",
Expand All @@ -1691,6 +1697,7 @@
"vz-application-deployer-set-context-error-message": "Unable to deploy Verrazzano application because setting the Kubernetes client cluster context failed: {{error}}.",
"vz-application-deployer-install-check-failed-error-message": "Unable to deploy Verrazzano application because checking to see if Verrazzano is installed failed: {{error}}.",
"vz-application-deployer-not-installed-error-message": "Unable to deploy Verrazzano application because Verrazzano is not installed.",
"vz-application-deployer-verify-components-error-message": "Unable to deploy Verrazzano application because one or more referenced components are not deployed in the Kubernetes namespace {{namespace}}: {{error}}.",
"vz-application-deployer-deploy-project-in-progress": "Deploying Verrazzano Project",
"vz-application-deployer-deploy-project-error-message": "Unable to deploy Verrazzano application because an error occurred while deploying the Verrazzano project {{projectName}}: {{error}}.",
"vz-application-deployer-deploy-application-in-progress": "Deploying Application",
Expand Down
5 changes: 5 additions & 0 deletions electron/app/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,11 @@ class Main {
ipcMain.handle('deploy-verrazzano-project', async (event, kubectlExe, project, kubectlOptions) => {
return deployProject(kubectlExe, project, kubectlOptions);
});

// eslint-disable-next-line no-unused-vars
ipcMain.handle('verify-verrazzano-components-exist',async (event, kubectlExe, componentNames, namespace, kubectlOptions) => {
return kubectlUtils.verifyVerrazzanoComponentsDeployed(kubectlExe, componentNames, namespace, kubectlOptions);
});
}

async getLatestWdtInstaller(targetWindow) {
Expand Down
4 changes: 4 additions & 0 deletions webui/src/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -756,3 +756,7 @@ h6.wkt-panel-heading {
color: red;
font-weight: bold;
}

.wkt-placeholder-text {
color: var(--oj-label-inside-edge-color);
}
11 changes: 0 additions & 11 deletions webui/src/js/models/k8s-domain-definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,6 @@ define(['knockout', 'utils/observable-properties', 'utils/common-utilities', 'ut
this.updateSecrets();
});

this.configMapIsEmpty = () => {
let result = true;
for (const entry of wdtModel.getMergedPropertiesContent().observable()) {
if (entry.Override) {
result = false;
break;
}
}
return result;
};

this.readFrom = (json) => {
props.createGroup(name, this).readFrom(json);
};
Expand Down
4 changes: 0 additions & 4 deletions webui/src/js/models/vz-component-definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ define(['utils/observable-properties', 'utils/validation-helper'],
this.componentName = props.createProperty('${1}', k8sDomain.uid.observable);
this.componentName.addValidator(...validationHelper.getK8sNameValidators());

this.configMapIsEmpty = () => {
return k8sDomain.configMapIsEmpty();
};

this.readFrom = (json) => {
props.createGroup(name, this).readFrom(json);
};
Expand Down
2 changes: 1 addition & 1 deletion webui/src/js/utils/k8s-domain-configmap-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ define(['models/wkt-project', 'js-yaml'],
}

shouldCreateConfigMap() {
return this.project.settings.targetDomainLocation.value === 'mii' && !this.project.k8sDomain.configMapIsEmpty();
return this.project.settings.targetDomainLocation.value === 'mii';
}

generate(generateYaml = true) {
Expand Down
27 changes: 12 additions & 15 deletions webui/src/js/utils/k8s-domain-deployer.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,26 +247,24 @@ function (K8sDomainActionsBase, project, wktConsole, i18n, projectIo, dialogHelp
{domainName: domainUid, domainNamespace: domainNamespace});
dialogHelper.updateBusyDialog(busyDialogMessage, 12 / totalSteps);
if (this.project.settings.targetDomainLocation.value === 'mii') {
if (!this.project.k8sDomain.configMapIsEmpty()) {
const configMapData = this.k8sDomainConfigMapGenerator.generate().join('\n');
wktLogger.debug(configMapData);
const mapResults = await (window.api.ipc.invoke('k8s-apply', kubectlExe, configMapData, kubectlOptions));
if (!mapResults.isSuccess) {
const configMapName = this.project.k8sDomain.modelConfigMapName.value;
const errMessage = i18n.t('k8s-domain-deployer-create-config-map-failed-error-message',
{configMapName: configMapName, domainNamespace: domainNamespace, error: mapResults.reason});
dialogHelper.closeBusyDialog();
await window.api.ipc.invoke('show-error-message', errTitle, errMessage);
return Promise.resolve(false);
}
const configMapData = this.k8sDomainConfigMapGenerator.generate().join('\n');
wktLogger.debug(configMapData);
const mapResults = await (window.api.ipc.invoke('k8s-apply', kubectlExe, configMapData, kubectlOptions));
if (!mapResults.isSuccess) {
const configMapName = this.project.k8sDomain.modelConfigMapName.value;
const errMessage = i18n.t('k8s-domain-deployer-create-config-map-failed-error-message',
{configMapName: configMapName, domainNamespace: domainNamespace, error: mapResults.reason});
dialogHelper.closeBusyDialog();
await window.api.ipc.invoke('show-error-message', errTitle, errMessage);
return Promise.resolve(false);
}
}

// Deploy domain
busyDialogMessage = i18n.t('k8s-domain-deployer-deploy-in-progress',
{domainName: domainUid, domainNamespace: domainNamespace});
dialogHelper.updateBusyDialog(busyDialogMessage, 13 / totalSteps);
const domainSpecData = this.k8sDomainResourceGenerator.generate().join('\n');
const domainSpecData = new K8sDomainResourceGenerator(this.project.wko.installedVersion.value).generate().join('\n');
const domainResult = await (window.api.ipc.invoke('k8s-apply', kubectlExe, domainSpecData, kubectlOptions));
dialogHelper.closeBusyDialog();
if (domainResult.isSuccess) {
Expand Down Expand Up @@ -415,10 +413,9 @@ function (K8sDomainActionsBase, project, wktConsole, i18n, projectIo, dialogHelp
}
}

if (!this.project.k8sDomain.configMapIsEmpty()) {
if (this.project.settings.targetDomainLocation.value === 'mii') {
validationObject.addField('domain-design-configmap-label',
this.project.k8sDomain.modelConfigMapName.validate(true), domainFormConfig);
// The fields in the table should not require validation since no empty override values should be in this computed table.
}

return validationObject;
Expand Down
4 changes: 4 additions & 0 deletions webui/src/js/utils/observable-properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,10 @@ define(['knockout', 'utils/common-utilities', 'utils/validation-helper', 'utils/
this.observable.push(this.createArrayItem(this, !!item ? item : {}));
}

removeItemByIndex(index) {
this.observable.splice(index, 1);
}

get observable() {
if (this._observable == null) {
this._observable = ko.observableArray(this.createList(this._defaultValue));
Expand Down
32 changes: 29 additions & 3 deletions webui/src/js/utils/vz-application-deployer.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ define(['utils/vz-actions-base', 'models/wkt-project', 'models/wkt-console', 'ut
'utils/dialog-helper', 'utils/validation-helper', 'utils/vz-application-resource-generator',
'utils/vz-application-project-generator', 'utils/wkt-logger'],
function(VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, validationHelper,
VerrazzanoApplicationResourceGenerator, VerrazzanoProjectResourceGenerator) {
VerrazzanoApplicationResourceGenerator, VerrazzanoProjectResourceGenerator, wktLogger) {
class VzApplicationDeployer extends VzActionsBase {
constructor() {
super();
Expand All @@ -35,7 +35,7 @@ function(VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, vali

const createProject = this.project.vzApplication.useMultiClusterApplication.value && this.project.vzApplication.createProject.value;

const totalSteps = createProject ? 6.0 : 5.0;
const totalSteps = createProject ? 7.0 : 6.0;
const kubectlExe = this.getKubectlExe();
try {
let busyDialogMessage = i18n.t('flow-validate-kubectl-exe-in-progress');
Expand Down Expand Up @@ -86,7 +86,25 @@ function(VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, vali
}
}

let step = 4;
// Verify that all referenced components are already deployed in the namespace.
const namespace = this.project.k8sDomain.kubernetesNamespace.value;
busyDialogMessage = i18n.t('flow-checking-vz-app-components-deployed-in-progress');
dialogHelper.updateBusyDialog(busyDialogMessage, 4/totalSteps);
if (!options.skipVzComponentsDeployedCheck) {
const appComponentNames = this.getApplicationComponentNames();
wktLogger.debug('appComponentNames = %s', appComponentNames);
const result = await window.api.ipc.invoke('verify-verrazzano-components-exist',
kubectlExe, appComponentNames, namespace, kubectlOptions);
if (!result.isSuccess) {
dialogHelper.closeBusyDialog();
const errMessage = i18n.t('vz-application-deployer-verify-components-error-message',
{ namespace: namespace, error: result.reason });
await window.api.ipc.invoke('show-error-message', errTitle, errMessage);
return Promise.resolve(false);
}
}

let step = 5;
if (createProject) {
const vzProjectGenerator = new VerrazzanoProjectResourceGenerator();
const projectSpec = vzProjectGenerator.generate().join('\n');
Expand Down Expand Up @@ -153,8 +171,16 @@ function(VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, vali
this.project.vzApplication.projectName.validate(true), vzApplicationFormConfig);
}

// Don't allow an application with zero components...
validationObject.addField('vz-application-design-components-label',
validationHelper.validateRequiredField(this.project.vzApplication.components.value), vzApplicationFormConfig);

return validationObject;
}

getApplicationComponentNames() {
return this.project.vzApplication.components.value.map(component => component.name);
}
}
return new VzApplicationDeployer();
});
4 changes: 4 additions & 0 deletions webui/src/js/utils/vz-component-configmap-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ define(['models/wkt-project', 'utils/k8s-domain-configmap-generator', 'utils/vz-
this.configMapComponentNamespace = '';
}

shouldCreateConfigMap() {
return this.k8sDomainConfigMapGenerator.shouldCreateConfigMap();
}

generate() {
const k8sDomainConfigMap = this.k8sDomainConfigMapGenerator.generate(false);

Expand Down
5 changes: 2 additions & 3 deletions webui/src/js/utils/vz-component-deployer.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ function(VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, vali
const vzResourceGenerator = new VerrazzanoComponentResourceGenerator();
const vzConfigMapGenerator = new VerrazzanoComponentConfigMapGenerator();
const components = [ vzResourceGenerator.generate().join('\n') ];
if (!this.project.vzComponent.configMapIsEmpty()) {
if (vzConfigMapGenerator.shouldCreateConfigMap()) {
wktLogger.debug('Adding ConfigMap for component deployment');
components.push(vzConfigMapGenerator.generate().join('\n'));
}
Expand Down Expand Up @@ -326,10 +326,9 @@ function(VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper, vali
}
}

if (!this.project.vzComponent.configMapIsEmpty()) {
if (this.project.settings.targetDomainLocation.value === 'mii') {
validationObject.addField('domain-design-configmap-label',
this.project.k8sDomain.modelConfigMapName.validate(true), vzComponentFormConfig);
// The fields in the table should not require validation since no empty override values should be in this computed table.
}

return validationObject;
Expand Down
3 changes: 2 additions & 1 deletion webui/src/js/utils/vz-component-undeployer.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ function(VzActionsBase, project, wktConsole, i18n, projectIo, dialogHelper) {
// Prompt user to remove just the domain or the entire domain namespace.
const componentName = this.project.vzComponent.componentName.value;
const componentNamespace = this.project.k8sDomain.kubernetesNamespace.value;
const configMapName = this.project.vzComponent.configMapIsEmpty() ? undefined : this.project.k8sDomain.modelConfigMapName.value;
const configMapName = this.project.settings.targetDomainLocation.value === 'mii' ?
this.project.k8sDomain.modelConfigMapName.value : undefined;

const promptTitle = i18n.t('vz-component-undeployer-remove-namespace-prompt-title');
const promptQuestion = this._getPromptQuestion(componentName, componentNamespace, configMapName);
Expand Down
Loading