Skip to content

Commit

Permalink
Merge pull request #522 from zowe/bugfix/v2/multiple-discovery
Browse files Browse the repository at this point in the history
Fixes and enhancements for handling multiple discovery servers
  • Loading branch information
1000TurquoisePogs authored Jan 9, 2024
2 parents 565283d + 33d0e55 commit bba0d74
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 38 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ All notable changes to the Zlux Server Framework package will be documented in t
This repo is part of the app-server Zowe Component, and the change logs here may appear on Zowe.org in that section.

## 2.14.0
- Bugfix: App-server could not load when multiple discovery servers were present and the app-server was unable to reach the first one specified. Now, the app-server will iterate through the list of servers until an accessible one is reached. (#522)
- Bugfix: App-server would not correctly detect when it was running in a high-availability configuration environment. (#521)
- Bugfix: A call to GET /plugins would trigger an authorization check regardless of if rbac was set on or off (#523)

Expand Down
63 changes: 33 additions & 30 deletions lib/apiml.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,9 @@ const MEDIATION_LAYER_INSTANCE_DEFAULTS = (zluxProto, zluxHostname, zluxPort) =>
}
}};

function ApimlConnector({ hostName, port, isHttps, discoveryHost,
function ApimlConnector({ hostName, port, isHttps, discoveryUrls,
discoveryPort, tlsOptions, eurekaOverrides }) {
Object.assign(this, { hostName, port, isHttps, discoveryHost,
Object.assign(this, { hostName, port, isHttps, discoveryUrls,
discoveryPort, tlsOptions, eurekaOverrides });
this.vipAddress = hostName;
}
Expand Down Expand Up @@ -110,23 +110,13 @@ ApimlConnector.prototype = {
const end = Date.now() + timer;

return new Promise((resolve, reject) => {
const options = Object.assign({
host: this.discoveryHost,
port: this.discoveryPort,
method: 'GET',
path: `/eureka/apps/${serviceName}`,
headers: {'accept':'application/json'}
}, this.tlsOptions);

if (!this.tlsOptions.rejectUnauthorized) {
//Keeping these certs causes an openssl error 46, unknown cert error in a dev environment
delete options.cert;
delete options.key;
} //else, apiml expects a cert and will give a 403.
const optionsArray = this.getRequestOptionsArray('GET', `/eureka/apps/${serviceName}`);
let optionsIndex = 0;

const issueRequest = () => {
const options = optionsArray[optionsIndex];
if (Date.now() > end) {
log.warn(`ZWED0045`, this.discoveryHost, this.discoveryPort);
log.warn(`ZWED0045W`, this.discoveryHost, this.discoveryPort);
return reject(new Error(`Call timeout when fetching agent status from APIML`));
}

Expand Down Expand Up @@ -156,7 +146,9 @@ ApimlConnector.prototype = {
reject(new Error(`Call timeout when fetching agent status from APIML`));
});
req.on('error', (error) => {
log.warn("APIML query error:", error.message);
log.warn("ZWED0180W", options.host, options.port, error.message);
//
optionsIndex = (optionsIndex+1) % optionsArray.length;
setTimeout(issueRequest, AGENT_CHECK_RECONNECT_DELAY);
});
req.end();
Expand Down Expand Up @@ -249,8 +241,7 @@ ApimlConnector.prototype = {
}
log.debug("ZWED0144I", JSON.stringify(zluxProxyServerInstanceConfig, null, 2)); //log.debug("zluxProxyServerInstanceConfig: "
//+ JSON.stringify(zluxProxyServerInstanceConfig, null, 2))
const defaultUrl = `https://${this.discoveryHost}:${this.discoveryPort}/eureka/apps`;
const serviceUrls = this.getServiceUrls(defaultUrl);
const serviceUrls = this.getServiceUrls();
zluxProxyServerInstanceConfig.eureka.serviceUrls = { default: serviceUrls };
log.info(`ZWED0020I`, serviceUrls.join(',')); //log.info(`Registering at ${url}...`);
log.debug("ZWED0145I", JSON.stringify(zluxProxyServerInstanceConfig)); //log.debug(`zluxProxyServerInstanceConfig ${JSON.stringify(zluxProxyServerInstanceConfig)}`)
Expand Down Expand Up @@ -285,17 +276,29 @@ ApimlConnector.prototype = {
});
},

getServiceUrls(defaultUrl) {
const discoveryServiceList = process.env['ZWE_DISCOVERY_SERVICES_LIST'] || '';
const serviceUrls = discoveryServiceList
.split(',')
.map(url => url.trim())
.filter(url => url.length > 0)
.map(url => url + (url.endsWith('/') ? '' : '/') + 'apps');
if (serviceUrls.length === 0) {
serviceUrls.push(defaultUrl);
}
return serviceUrls;
getServiceUrls() {
return this.discoveryUrls.map(url => url + (url.endsWith('/') ? '' : '/') + 'apps');
},

getRequestOptionsArray(method, path) {
return this.discoveryUrls.map((url)=>{
//in the form of https://host:port/eureka/, trim from https:// and following slash.
const hostAndPort = url.substring(8, url.indexOf('/', 8)).split(':');
const options = Object.assign({
host: hostAndPort[0],
port: hostAndPort[1],
method: method,
path: path,
headers: {'accept':'application/json'}
}, this.tlsOptions);

if (!this.tlsOptions.rejectUnauthorized) {
//Keeping these certs causes an openssl error 46, unknown cert error in a dev environment
delete options.cert;
delete options.key;
} //else, apiml expects a cert and will give a 403.
return options;
});
}

/*
Expand Down
1 change: 1 addition & 0 deletions lib/assets/i18n/log/messages_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@
"ZWED0177W":"Unable to load %s for '%s' into config",
"ZWED0178W":"Skipping authentication plugin %s because it's not HA compatible",
"ZWED0179W":"Unable to retrieve the list of certificate authorities from the keyring=%s owner=%s Error: %s",
"ZWED0180W":"Failed to query discovery server (%s:%s) for agent access: %s",

"ZWED0001E":"RESERVED: Error: %s",
"ZWED0002E":"Could not stop language manager for types=%s",
Expand Down
5 changes: 2 additions & 3 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,7 @@ Server.prototype = {
hostName: webAppOptions.hostname,
port: this.port,
isHttps: util.isServerHttps(this.zoweConfig),
discoveryHost: apimlConfig.server.hostname,
discoveryPort: apimlConfig.server.port,
discoveryUrls: apimlConfig.server.discoveryUrls || [`https://${apimlConfig.server.hostname}:${apimlConfig.server.port}/eureka/`],
tlsOptions: this.tlsOptions,
eurekaOverrides: apimlConfig.eureka
});
Expand All @@ -236,7 +235,7 @@ Server.prototype = {
&& this.componentConfig.agent.mediationLayer.serviceName
&& this.componentConfig.node.mediationLayer.server?.gatewayPort) {
//at this point, we expect zss to also be attached to the mediation layer, so lets adjust.
webAppOptions.proxiedHost = apimlConfig.server.hostname;
webAppOptions.proxiedHost = apimlConfig.server.gatewayHostname;
webAppOptions.proxiedPort = this.componentConfig.node.mediationLayer.server.gatewayPort;
if (firstWorker) {
yield this.apiml.checkAgent(this.componentConfig.agent.handshakeTimeout,
Expand Down
3 changes: 2 additions & 1 deletion lib/webapp.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,8 @@ function getUserEnv(rbac, zoweConfig){
"ZWED_node_mediationLayer_enabled": nodeConfig.mediationLayer.enabled,

"ZWED_node_mediationLayer_server_hostname": nodeConfig.mediationLayer.server.hostname,

"ZWED_node_mediationLayer_server_gatewayHostname": nodeConfig.mediationLayer.server.gatewayHostname,

//may diverge from above
"ZWE_EXTERNAL_HOSTS": process.env.ZWE_EXTERNAL_HOSTS ? process.env.ZWE_EXTERNAL_HOSTS : zoweConfig.zowe.externalDomains.join(','),
"ZWE_zowe_externalDomains": zoweConfig.zowe.externalDomains.join(','),
Expand Down
6 changes: 3 additions & 3 deletions plugins/sso-auth/lib/apimlHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class ApimlHandler {
constructor(pluginDef, pluginConf, serverConf, context) {
this.logger = context.logger;
this.apimlConf = serverConf.node.mediationLayer.server;
this.gatewayUrl = `https://${this.apimlConf.hostname}:${this.apimlConf.gatewayPort}`;
this.gatewayUrl = `https://${this.apimlConf.gatewayHostname}:${this.apimlConf.gatewayPort}`;
this.httpsAgent = new https.Agent(context.tlsOptions);
}

Expand All @@ -66,7 +66,7 @@ class ApimlHandler {
}
const gatewayUrl = this.gatewayUrl;
const options = {
hostname: this.apimlConf.hostname,
hostname: this.apimlConf.gatwayHostname,
port: this.apimlConf.gatewayPort,
//TODO uncertainty about using apicatalog route instead of something part of the gateway itself
path: '/apicatalog/api/v1/auth/logout',
Expand Down Expand Up @@ -194,7 +194,7 @@ class ApimlHandler {
}

return {
hostname: this.apimlConf.hostname,
hostname: this.apimlConf.gatewayHostname,
port: this.apimlConf.gatewayPort,
path: path,
method: method,
Expand Down
2 changes: 1 addition & 1 deletion plugins/sso-auth/lib/ssoAuth.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const apimlHandlerFactory = require('./apimlHandler');
function doesApimlExist(serverConf) {
return ((serverConf.node.mediationLayer !== undefined)
&& (serverConf.node.mediationLayer.server !== undefined)
&& (serverConf.node.mediationLayer.server.hostname !== undefined)
&& (serverConf.node.mediationLayer.server.gatewayHostname !== undefined)
&& (serverConf.node.mediationLayer.server.gatewayPort !== undefined)
&& (serverConf.node.mediationLayer.server.port !== undefined)
&& (serverConf.node.mediationLayer.enabled == true))
Expand Down

0 comments on commit bba0d74

Please sign in to comment.