Skip to content
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

Fixes and enhancements for handling multiple discovery servers #522

Merged
merged 4 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ This repo is part of the app-server Zowe Component, and the change logs here may

## 2.14.0
- Bugfix: App-server would not correctly detect when it was running in a high-availability configuration environment.
- 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.

## 2.13.0
- Added support for using zowe.network and components.app-server.zowe.network to set listener IP and TLS properties including max and min version, ciphers, and ECDH curves. (#511)
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
Loading