Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 50dd192

Browse files
Add endpoint URL to ingress rule table; retrieve hosts on demand (#215)
* Use kubernetes context names data provider to populate chooser * Add endpoint URL to ingress rule table; retrieve hosts on demand * Combine and resize columns in the ingress rules table * Determine the generated host name based on the external address
1 parent 687d2a2 commit 50dd192

File tree

11 files changed

+217
-52
lines changed

11 files changed

+217
-52
lines changed

‎electron/app/js/ipcRendererPreload.js‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ contextBridge.exposeInMainWorld(
236236
'undeploy-verrazzano-components',
237237
'get-verrazzano-component-names',
238238
'get-verrazzano-secret-names',
239+
'get-verrazzano-host-names',
239240
'get-verrazzano-cluster-names',
240241
'get-verrazzano-deployment-names-all-namespaces',
241242
'verify-verrazzano-components-exist',

‎electron/app/js/kubectlUtils.js‎

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,26 +1090,44 @@ async function getVerrazzanoApplicationHostnames(kubectlExe, applicationName, ap
10901090
};
10911091

10921092
return new Promise(resolve => {
1093-
executeFileCommand(kubectlExe, args, env).then(gatewayJson => {
1094-
const gateway = JSON.parse(gatewayJson);
1095-
const serversArray = gateway.spec?.servers;
1096-
if (Array.isArray(serversArray) && serversArray.length > 0) {
1097-
const hostsArray = serversArray[0].hosts;
1098-
if (Array.isArray(hostsArray) && hostsArray.length > 0) {
1099-
results.hostnames = hostsArray;
1100-
}
1101-
}
1102-
if (results.hostnames) {
1103-
results.reason = i18n.t('kubectl-get-vz-app-hostnames-not-found',
1104-
{ appName: applicationName, appNamespace: applicationNamespace });
1093+
// the external address will help to determine the generated application host name.
1094+
// this address is returned as the generated host name if no matching DNS name is found.
1095+
getVerrazzanoIngressExternalAddress(kubectlExe, options).then(externalAddressResults => {
1096+
if(!externalAddressResults.isSuccess) {
1097+
resolve(externalAddressResults);
1098+
return;
11051099
}
1106-
resolve(results);
1107-
}).catch(err => {
1108-
results.isSuccess = false;
1109-
results.reason =
1110-
i18n.t('kubectl-get-vz-app-hostnames-error-message',
1111-
{ appName: applicationName, appNamespace: applicationNamespace, error: getErrorMessage(err) });
1112-
resolve(results);
1100+
const externalAddress = externalAddressResults.externalAddress;
1101+
results.generatedHostname = externalAddress;
1102+
1103+
executeFileCommand(kubectlExe, args, env).then(gatewayJson => {
1104+
const gateway = JSON.parse(gatewayJson);
1105+
const serversArray = gateway.spec?.servers;
1106+
if (Array.isArray(serversArray) && serversArray.length > 0) {
1107+
const hostsArray = serversArray[0].hosts;
1108+
if (Array.isArray(hostsArray) && hostsArray.length > 0) {
1109+
results.hostnames = hostsArray;
1110+
1111+
// see if a DNS name contains the external address
1112+
for(const hostname of hostsArray) {
1113+
if(hostname.includes(externalAddress)) {
1114+
results.generatedHostname = hostname;
1115+
}
1116+
}
1117+
}
1118+
}
1119+
if (!results.hostnames) {
1120+
results.reason = i18n.t('kubectl-get-vz-app-hostnames-not-found',
1121+
{ appName: applicationName, appNamespace: applicationNamespace });
1122+
}
1123+
resolve(results);
1124+
}).catch(err => {
1125+
results.isSuccess = false;
1126+
results.reason =
1127+
i18n.t('kubectl-get-vz-app-hostnames-error-message',
1128+
{ appName: applicationName, appNamespace: applicationNamespace, error: getErrorMessage(err) });
1129+
resolve(results);
1130+
});
11131131
});
11141132
});
11151133
}

‎electron/app/js/vzUtils.js‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,17 @@ async function getSecretNamesByNamespace(kubectlExe, namespace, kubectlOptions)
8282
});
8383
}
8484

85+
async function getHostNames(kubectlExe, applicationName, applicationNamespace, options) {
86+
return new Promise(resolve => {
87+
kubectlUtils.getVerrazzanoApplicationHostnames(kubectlExe, applicationName, applicationNamespace, options).then(result => {
88+
if (!result.isSuccess) {
89+
return resolve(result);
90+
}
91+
resolve(result);
92+
});
93+
});
94+
}
95+
8596
async function getVerrazzanoClusterNames(kubectlExe, kubectlOptions) {
8697
return new Promise(resolve => {
8798
kubectlUtils.getKubernetesObjectsByNamespace(kubectlExe, kubectlOptions, 'VerrazzanoManagedCluster', 'verrazzano-mc').then(result => {
@@ -116,6 +127,7 @@ module.exports = {
116127
deployProject,
117128
getComponentNamesByNamespace,
118129
getDeploymentNamesFromAllNamespaces,
130+
getHostNames,
119131
getSecretNamesByNamespace,
120132
getVerrazzanoClusterNames,
121133
undeployApplication,

‎electron/app/locales/en/electron.json‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,8 @@
328328
"kubectl-verify-vz-components-deployed-error-message": "Unable to find one or more components in namespace {{namespace}}: {{missingComponents}}",
329329
"kubectl-get-vz-ingress-external-address-not-found": "Unable to find the External IP Address in the Istio Gateway service {{gatewayService}} in Kubernetes namespace {{gatewayNamespace}}",
330330
"kubectl-get-vz-ingress-external-address-error-message": "Failed to find the External IP Address in the Istio Gateway service {{gatewayService}} in Kubernetes namespace {{gatewayNamespace}}: {{error}}",
331+
"kubectl-get-vz-app-hostnames-error-message": "Unable to get Verrazzano application host names: {{error}}",
332+
"kubectl-get-vz-app-hostnames-not-found": "Verrazzano application host names not found",
331333

332334
"helm-not-specified-error-message": "Helm executable path was not provided",
333335
"helm-not-exists-error-message": "Helm executable {{filePath}} does not exist",

‎electron/app/locales/en/webui.json‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,9 @@
16431643
"vz-application-design-choose-clusters-dialog-title": "Choose Verrazzano Clusters for Application Placement",
16441644
"vz-application-design-choose-clusters-name-label": "Verrazzano Cluster Names",
16451645
"vz-application-design-choose-clusters-name-help": "Select the Verrazzano Clusters to use for application placement.",
1646+
"vz-application-design-get-hosts-error-message": "Unable get hosts for the application: {{error}}.",
1647+
"vz-application-design-get-hosts-error-title": "Get Hosts for Application Aborted",
1648+
"vz-application-design-get-hosts-in-progress": "Getting Hosts for Application",
16461649

16471650
"vz-application-design-component-ingress-trait-enabled-label": "Enable Ingress Trait",
16481651
"vz-application-design-component-ingress-trait-enabled-help": "Enable the Ingress Trait for this component.",
@@ -1656,9 +1659,10 @@
16561659
"vz-application-design-ingress-trait-rules-hosts-label": "Hosts",
16571660
"vz-application-design-ingress-trait-rules-first-path-type-label": "First Path Type",
16581661
"vz-application-design-ingress-trait-rules-first-path-label": "First Path",
1662+
"vz-application-design-ingress-trait-rules-first-path-url-label": "URL",
1663+
"vz-application-design-ingress-trait-rules-update-urls-button-label": "Update URLs",
16591664
"vz-application-design-ingress-trait-rule-edit-destination-title": "Destination",
1660-
"vz-application-design-ingress-trait-rules-destination-host-label": "Destination Host",
1661-
"vz-application-design-ingress-trait-rules-destination-port-label": "Destination Port",
1665+
"vz-application-design-ingress-trait-rules-destination-label": "Destination",
16621666
"vz-application-design-add-rule-tooltip": "Add Ingress Rule",
16631667
"vz-application-design-edit-rule-tooltip": "Edit Ingress Rule",
16641668
"vz-application-design-delete-rule-tooltip": "Delete Ingress Rule",

‎electron/app/main.js‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ const { startWebLogicRemoteConsoleBackend, getDefaultDirectoryForOpenDialog, set
4040
const { getVerrazzanoReleaseVersions, isVerrazzanoInstalled, installVerrazzanoPlatformOperator,
4141
verifyVerrazzanoPlatformOperatorInstall, installVerrazzano, verifyVerrazzanoInstallStatus } = require('./js/vzInstaller');
4242
const { deployApplication, deployComponents, deployProject, getComponentNamesByNamespace, getSecretNamesByNamespace,
43-
getVerrazzanoClusterNames, getDeploymentNamesFromAllNamespaces, undeployApplication, undeployComponents } = require('./js/vzUtils');
43+
getVerrazzanoClusterNames, getDeploymentNamesFromAllNamespaces, undeployApplication, undeployComponents, getHostNames
44+
} = require('./js/vzUtils');
4445

4546
const { getHttpsProxyUrl, getBypassProxyHosts } = require('./js/userSettings');
4647
const { sendToWindow } = require('./js/windowUtils');
@@ -1081,6 +1082,11 @@ class Main {
10811082
return getSecretNamesByNamespace(kubectlExe, namespace, kubectlOptions);
10821083
});
10831084

1085+
// eslint-disable-next-line no-unused-vars
1086+
ipcMain.handle('get-verrazzano-host-names', async (event, kubectlExe, applicationName, applicationNamespace, options) => {
1087+
return getHostNames(kubectlExe, applicationName, applicationNamespace, options);
1088+
});
1089+
10841090
// eslint-disable-next-line no-unused-vars
10851091
ipcMain.handle('get-verrazzano-cluster-names', async (event, kubectlExe, kubectlOptions) => {
10861092
return getVerrazzanoClusterNames(kubectlExe, kubectlOptions);

‎webui/src/css/app.css‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,10 @@ h6.wkt-panel-heading {
583583
margin-bottom: -10px;
584584
}
585585

586+
.wkt-header-button-row.wkt-header-with-margin .oj-button {
587+
margin-bottom: 10px;
588+
}
589+
586590
.wkt-header-button-row h6 {
587591
flex: 1 1 auto;
588592
}

‎webui/src/js/models/vz-application-definition.js‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
/**
22
* @license
3-
* Copyright (c) 2022, Oracle and/or its affiliates.
3+
* Copyright (c) 2022, 2023, Oracle and/or its affiliates.
44
* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
55
*/
66
'use strict';
77

8-
define(['utils/observable-properties', 'utils/validation-helper', 'utils/wkt-logger'],
9-
function(props, validationHelper) {
8+
define(['utils/observable-properties', 'utils/validation-helper', 'knockout','utils/wkt-logger'],
9+
function(props, validationHelper,ko) {
1010
return function (name, k8sDomain) {
1111
function VerrazzanoApplicationModel() {
1212
let componentChanged = false;
@@ -30,6 +30,10 @@ define(['utils/observable-properties', 'utils/validation-helper', 'utils/wkt-log
3030
'loggingTraitEnabled', 'loggingTraitImage', 'loggingTraitConfiguration' ];
3131
this.components = props.createListProperty(this.componentKeys).persistByKey('name');
3232

33+
// this is a transient ko observable that is not persisted
34+
this.hosts = ko.observableArray();
35+
this.generatedHost = ko.observable();
36+
3337
this.readFrom = (json) => {
3438
props.createGroup(name, this).readFrom(json);
3539
};

‎webui/src/js/viewModels/vz-application-design-view.js‎

Lines changed: 118 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @license
3-
* Copyright (c) 2022, Oracle and/or its affiliates.
3+
* Copyright (c) 2022, 2023, Oracle and/or its affiliates.
44
* Licensed under The Universal Permissive License (UPL), Version 1.0 as shown at https://oss.oracle.com/licenses/upl/
55
*/
66
define(['models/wkt-project', 'accUtils', 'utils/common-utilities', 'knockout', 'utils/i18n', 'ojs/ojbufferingdataprovider',
@@ -432,23 +432,23 @@ function (project, accUtils, utils, ko, i18n, BufferingDataProvider, ArrayDataPr
432432
this.ingressTraitRulesColumnData = [
433433
{
434434
'headerText': this.labelMapper('ingress-trait-rules-hosts-label'),
435-
'sortable': 'disable',
436-
},
437-
{
438-
'headerText': this.labelMapper('ingress-trait-rules-first-path-type-label'),
439-
'sortable': 'disable',
435+
'resizable': 'enabled',
436+
'sortable': 'disable'
440437
},
441438
{
442439
'headerText': this.labelMapper('ingress-trait-rules-first-path-label'),
443-
'sortable': 'disable',
440+
'resizable': 'enabled',
441+
'sortable': 'disable'
444442
},
445443
{
446-
'headerText': this.labelMapper('ingress-trait-rules-destination-host-label'),
444+
'headerText': this.labelMapper('ingress-trait-rules-first-path-url-label'),
445+
'resizable': 'enabled',
447446
'sortable': 'disable',
447+
'width': '35%'
448448
},
449449
{
450-
'headerText': this.labelMapper('ingress-trait-rules-destination-port-label'),
451-
'sortable': 'disable',
450+
'headerText': this.labelMapper('ingress-trait-rules-destination-label'),
451+
'sortable': 'disable'
452452
},
453453
{
454454
'className': 'wkt-table-delete-cell',
@@ -468,14 +468,119 @@ function (project, accUtils, utils, ko, i18n, BufferingDataProvider, ArrayDataPr
468468
},
469469
];
470470

471-
this.getFirstPathField = (paths, fieldName) => {
472-
let result;
471+
// display in the first path column, example "/path (regex)"
472+
this.getFirstPathText = (rowData) => {
473+
let result = null;
474+
const paths = rowData.paths;
473475
if (Array.isArray(paths) && paths.length > 0) {
474-
result = paths[0][fieldName];
476+
result = paths[0].path;
477+
if(result && result.length) {
478+
const pathType = paths[0].pathType;
479+
if(pathType && pathType !== 'exact') {
480+
result += ` (${pathType})`;
481+
}
482+
}
483+
}
484+
return result;
485+
};
486+
487+
// display in the destination column, example "host:port"
488+
this.getDestinationText = (rowData) => {
489+
let result = rowData.destinationHost;
490+
if(result) {
491+
const port = rowData.destinationPort;
492+
if(port != null) {
493+
result += `:${port}`;
494+
}
475495
}
476496
return result;
477497
};
478498

499+
function getRuleHost(rowData) {
500+
const ruleHostsText = rowData.hosts;
501+
if(ruleHostsText) {
502+
const ruleHosts = ruleHostsText.split(',').map(host => host.trim());
503+
if(ruleHosts.length) {
504+
return ruleHosts[0];
505+
}
506+
}
507+
return null;
508+
}
509+
510+
this.computedUrl = (rowData) => {
511+
return ko.computed(() => {
512+
let urlHost = '<host>';
513+
const generatedHost = project.vzApplication.generatedHost();
514+
if(generatedHost && generatedHost.length) {
515+
urlHost = generatedHost;
516+
}
517+
518+
const ruleHost = getRuleHost(rowData);
519+
if(ruleHost) {
520+
urlHost = ruleHost;
521+
}
522+
523+
let result = 'https://' + urlHost;
524+
525+
let urlPath = '<path>';
526+
const paths = rowData.paths;
527+
if(paths && paths.length) {
528+
urlPath = paths[0].path;
529+
if(urlPath && urlPath.length) {
530+
result += urlPath;
531+
}
532+
}
533+
534+
return result;
535+
});
536+
};
537+
538+
// resolves to true if the row data can make a clickable link
539+
this.computedCanLink = (rowData) => {
540+
return ko.computed(() => {
541+
const appHosts = project.vzApplication.hosts();
542+
if(!appHosts.length) {
543+
return false;
544+
}
545+
546+
const ruleHost = getRuleHost(rowData);
547+
if(ruleHost && !appHosts.includes(ruleHost)) {
548+
return false;
549+
}
550+
551+
const paths = rowData.paths;
552+
if(!paths || !paths.length) {
553+
return false;
554+
}
555+
556+
return paths[0].pathType !== 'regex';
557+
});
558+
};
559+
560+
this.updateUrls = async() => {
561+
const busyDialogMessage = this.labelMapper('get-hosts-in-progress');
562+
dialogHelper.openBusyDialog(busyDialogMessage, 'bar', 1 / 2.0);
563+
564+
const kubectlExe = this.project.kubectl.executableFilePath.value;
565+
const kubectlOptions = k8sHelper.getKubectlOptions();
566+
const applicationName = project.vzApplication.applicationName.value;
567+
const applicationNamespace = project.k8sDomain.kubernetesNamespace.value;
568+
const hostsResult = await window.api.ipc.invoke('get-verrazzano-host-names', kubectlExe, applicationName,
569+
applicationNamespace, kubectlOptions);
570+
571+
dialogHelper.closeBusyDialog();
572+
573+
if (!hostsResult.isSuccess) {
574+
const errTitle = 'vz-application-design-get-hosts-error-title';
575+
const errMessage = this.labelMapper('get-hosts-error-message', { error: hostsResult.reason });
576+
await window.api.ipc.invoke('show-error-message', errTitle, errMessage);
577+
return;
578+
}
579+
580+
project.vzApplication.hosts(hostsResult.hostnames);
581+
project.vzApplication.generatedHost(hostsResult.generatedHostname);
582+
};
583+
479584
this.componentsIngressTraitRulesDataProvider = (component) => {
480585
const key = component.name;
481586
let provider = this.componentIngressTraitRulesDataProviders[key];

‎webui/src/js/views/choose-kubectl-context-dialog.html‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
</div>
1111
<div slot="body">
1212
<div class="oj-panel">
13-
<oj-form-layout max-columns="2" direction="row">
13+
<oj-form-layout max-columns="1" direction="row">
1414
<oj-select-single label-hint="[[labelMapper('name-label')]]"
1515
value="{{selectedKubectlContextName}}"
16-
data="{{availableClusterNamesDP}}"
16+
data="{{availableKubectlContextNamesDP}}"
1717
help.instruction="[[labelMapper('name-help')]]">
1818
</oj-select-single>
1919
</oj-form-layout>

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /