diff --git a/documentation/staging/content/userguide/istio/istio.md b/documentation/staging/content/userguide/istio/istio.md
index 14e43d4dcbd..99adc3d7a02 100644
--- a/documentation/staging/content/userguide/istio/istio.md
+++ b/documentation/staging/content/userguide/istio/istio.md
@@ -142,6 +142,38 @@ If the WebLogic administration port is enabled on the Administration Server:
Additionally, when Istio support is enabled for a domain, the operator
ensures that the Istio sidecar is not injected into the introspector job's pods.
+#### Support for network changes in Istio v1.10 and later
+
+Starting with Istio 1.10, the networking behavior was changed in that the proxy no longer redirects
+the traffic to the localhost interface, but instead forwards it to the network interface associated
+with the pod's IP.
+
+To learn more about changes to Istio networking beginning with Istio 1.10, see [Upcoming networking changes in Istio 1.10](https://istio.io/latest/blog/2021/upcoming-networking-changes/).
+
+In order to support Istio v1.10 and later, as well as previous releases, the
+operator will:
+
+* Add an additional WebLogic HTTP protocol network channel for the readiness probe that is bound to the localhost network interface.
+* Add additional WebLogic network channels, bound to the localhost network interface, for each defined custom network channel.
+* Continue to automatically add the network channels described above in [How Istio-enabled domains differ from regular domains](#how-istio-enabled-domains-differ-from-regular-domains)
+
+When adding additional WebLogic network channels for the readiness probe and any defined custom channels,
+the name of the additional channel will be appended with '-lhNN', where NN represents
+a two digit value for uniqueness.
+
+For example, the additional WebLogic HTTP protocol network channel for the readiness probe would be
+defined as follows:
+
+|Name|Port|Listening address|Protocol|Exposed as a container port|
+|----|----|----|--------|-----|
+|`http-probe-lh01`|From configuration Istio `readinessPort` | `127.0.0.1` | `http`| No |
+
+As another example, for a custom WebLogic network channel defined as `T3Channel` with port `5556`
+and protocol `t3`, the additional channel would be defined as follows:
+
+|Name|Port|Listening address|Protocol|Exposed as a container port|
+|----|----|----|--------|-----|
+|`T3Channel-lh01`| `5556` | `127.0.0.1` | `t3`| Yes |
### Apply the Domain YAML file
diff --git a/operator/src/main/resources/scripts/introspectDomain.py b/operator/src/main/resources/scripts/introspectDomain.py
index 1098a053014..9e08834e96e 100644
--- a/operator/src/main/resources/scripts/introspectDomain.py
+++ b/operator/src/main/resources/scripts/introspectDomain.py
@@ -99,6 +99,8 @@
from utils import *
from weblogic.management.configuration import LegalHelper
+ISTIO_NAP_NAMES = ['tcp-cbt', 'tcp-ldap', 'tcp-iiop', 'tcp-snmp', 'http-default', 'tcp-default', 'https-secure', 'tls-ldaps', 'tls-default', 'tls-cbts', 'tls-iiops', 'https-admin']
+WLS_LOCALHOST_IDENTIFIER = '-lh'
class OfflineWlstEnv(object):
@@ -751,7 +753,8 @@ def addNetworkAccessPoint(self, server, nap, is_server_template):
if istio_enabled == 'true':
name = nap.getName()
- if name.startswith('http-') or name.startswith('tcp-') or name.startswith('tls-') or name.startswith('https-'):
+ if name.startswith('http-') or name.startswith('tcp-') or name.startswith('tls-') \
+ or name.startswith('https-') or nameContainsLocalHostIdentifier(name):
# skip istio ports already defined by WDT filtering for MII
return
http_protocol = [ 'http' ]
@@ -1056,6 +1059,222 @@ def writeListenAddress(self, originalValue, newValue):
repVerb="\"add\""
self.writeln("" + newValue + "")
+ def writeName(self, name):
+ self.writeln("" + name + "")
+
+ def writeProtocol(self, protocol):
+ self.writeln("" + protocol + "")
+
+ def writeListenPort(self, listen_port):
+ # offline WLST initializes int values to 0
+ if listen_port > 0:
+ self.writeln("" + str(listen_port) + "")
+
+ def writePublicAddress(self, public_address):
+ if public_address is not None and len(public_address) > 0:
+ self.writeln("" + public_address + "")
+
+ def writePublicPort(self, public_port):
+ # offline WLST initializes int values to 0
+ if public_port > 0:
+ self.writeln("" + str(public_port) + "")
+
+ def writeAcceptBacklog(self, accept_backlog):
+ # offline WLST initializes int values to 0
+ if accept_backlog > 0:
+ self.writeln("" + str(accept_backlog) + "")
+
+ def writeMaxBackoffBetweenFailures(self, maxBackoffBetweenFailures):
+ if maxBackoffBetweenFailures > 0:
+ self.writeln("" + str(maxBackoffBetweenFailures) + "")
+
+ def writeLoginTimeoutMillis(self, loginTimeoutMillis):
+ # offline WLST initializes int values to 0
+ if loginTimeoutMillis > 0:
+ self.writeln("" + str(loginTimeoutMillis) + "")
+
+ def writeCompleteMessageTimeout(self, completeMessageTimeout):
+ # offline WLST initializes int values to 0
+ if completeMessageTimeout > 0:
+ self.writeln("" + str(completeMessageTimeout) + "")
+
+ def writeIdleConnectionTimeout(self, idleConnectionTimeout):
+ # offline WLST initializes int values to 0
+ if idleConnectionTimeout > 0:
+ self.writeln("" + str(idleConnectionTimeout) + "")
+
+ def writeConnectTimeout(self, connectTimeout):
+ # offline WLST initializes int values to 0
+ if connectTimeout > 0:
+ self.writeln("" + str(connectTimeout) + "")
+
+ def writeTunnelingClientPingSecs(self, tunnelingClientPingSecs):
+ # offline WLST initializes int values to 0
+ if tunnelingClientPingSecs > 0:
+ self.writeln("" + str(tunnelingClientPingSecs) + "")
+
+ def writeTunnelingClientTimeoutSecs(self, tunnelingClientTimeoutSecs):
+ # offline WLST initializes int values to 0
+ if tunnelingClientTimeoutSecs > 0:
+ self.writeln("" + str(tunnelingClientTimeoutSecs) + "")
+
+ def writeMaxMessageSize(self, maxMessageSize):
+ # offline WLST initializes int values to 0
+ # Legal minimum max message size is 4096
+ # Allow WLS to check legal range
+ if maxMessageSize > 0:
+ self.writeln("" + str(maxMessageSize) + "")
+
+ def writeChannelWeight(self, channelWeight):
+ # default is 50
+ if channelWeight != 50:
+ self.writeln("" + str(channelWeight) + "")
+
+ def writeMaxConnectedClients(self, maxConnectedClients):
+ # default = java.lang.Integer.MAX_VALUE
+ if maxConnectedClients != 2147483647:
+ self.writeln("" + str(maxConnectedClients) + "")
+
+ def writeEnabled(self, nap):
+ # default enabled = 'true'
+ if nap.isEnabled() == false:
+ enabled = 'false'
+ self.writeln("" + enabled + "")
+
+ def writeTunnelingEnabled(self, nap):
+ # default tunnelingEnabled = 'false'
+ if nap.isTunnelingEnabled() == true:
+ tunnelingEnabled = 'true'
+ self.writeln("" + tunnelingEnabled + "")
+
+ def writeOutboundEnabled(self, nap):
+ # default outboundEnabled = 'false'
+ if nap.isOutboundEnabled() == true:
+ outboundEnabled = 'true'
+ self.writeln("" + outboundEnabled + "")
+
+ def writeUseFastSerialization(self, nap):
+ # default useFastSerialization = 'false'
+ if nap.getUseFastSerialization() == true:
+ useFastSerialization = 'true'
+ self.writeln("" + useFastSerialization + "")
+
+ def writeHttpEnabledForThisProtocol(self, nap):
+ # default httpEnabledForThisProtocol = 'true'
+ if nap.isHttpEnabledForThisProtocol() == false:
+ httpEnabledForThisProtocol = 'false'
+ self.writeln("" + httpEnabledForThisProtocol + "")
+
+ def writeTimeoutConnectionWithPendingResponses(self, nap):
+ # default timeoutConnectionWithPendingResponses = 'false'
+ if nap.getTimeoutConnectionWithPendingResponses() == true:
+ timeoutConnectionWithPendingResponses = 'true'
+ self.writeln("" + timeoutConnectionWithPendingResponses + "")
+
+ def writeSDPEnabled(self, nap):
+ # default sdpEnabled = 'false'
+ if nap.isSDPEnabled() == true:
+ sdpEnabled = 'true'
+ self.writeln("" + sdpEnabled + "")
+
+ def writeTwoWaySSLEnabled(self, nap):
+ # default twoWaySSLEnabled = 'false'
+ if nap.isTwoWaySSLEnabled() == true:
+ twoWaySSLEnabled = 'true'
+ self.writeln("" + twoWaySSLEnabled + "")
+
+ def writeClientCertificateEnforced(self, nap):
+ # default clientCertificateEnforced = 'false'
+ if nap.isClientCertificateEnforced() == true:
+ clientCertificateEnforced = 'true'
+ self.writeln("" + clientCertificateEnforced + "")
+
+ def writeChannelIdentityCustomized(self, nap):
+ # default channelIdentityCustomized = 'false'
+ if nap.isChannelIdentityCustomized() == true:
+ channelIdentityCustomized = 'true'
+ self.writeln("" + channelIdentityCustomized + "")
+
+ def writeCustomPrivateKeyAlias(self, nap):
+ customPrivateKeyAlias = nap.getCustomPrivateKeyAlias()
+ if customPrivateKeyAlias is not None:
+ self.writeln("" + customPrivateKeyAlias + "")
+
+ def writeCustomPrivateKeyPassPhraseEncrypted(self, nap):
+ customPriveKeyPassPhraseEncrypted = nap.getCustomPrivateKeyPassPhraseEncrypted()
+ if customPriveKeyPassPhraseEncrypted is not None and len(customPriveKeyPassPhraseEncrypted) > 0:
+ self.writeln("" + customPriveKeyPassPhraseEncrypted + "")
+
+ def writeCustomIdentityKeyStoreType(self, nap):
+ customIdentityKeyStoreType = nap.getCustomIdentityKeyStoreType()
+ if customIdentityKeyStoreType is not None and len(customIdentityKeyStoreType) > 0:
+ self.writeln("" + customIdentityKeyStoreType + "")
+
+ def writeCustomIdentityKeyStorePassPhraseEncrypted(self, nap):
+ customIdentityKeyStorePassPhraseEncrypted = nap.getCustomIdentityKeyStorePassPhraseEncrypted()
+ if customIdentityKeyStorePassPhraseEncrypted is not None and len(customIdentityKeyStorePassPhraseEncrypted) > 0:
+ self.writeln("" + customIdentityKeyStorePassPhraseEncrypted + "")
+
+ def writeHostnameVerificationIgnored(self, nap):
+ # default hostnameVerificationIgnored = 'false'
+ if nap.isHostnameVerificationIgnored() == true:
+ hostnameVerificationIgnored = 'true'
+ self.writeln("" + hostnameVerificationIgnored + "")
+
+ def writeHostnameVerifier(self, nap):
+ hostnameVerifier = nap.getHostnameVerifier()
+ if hostnameVerifier is not None and len(hostnameVerifier) > 0:
+ self.writeln("" + hostnameVerifier + "")
+
+ def writeCiphersuites(self, nap):
+ ciphersuites = nap.getCiphersuites()
+ if ciphersuites is not None:
+ for cipher in ciphersuites:
+ self.writeln("" + cipher + "")
+
+ def writeAllowUnencryptedNullCipher(self, nap):
+ # default allowUnencryptedNullCipher = 'false'
+ if nap.isAllowUnencryptedNullCipher() == true:
+ allowUnencryptedNullCipher = 'true'
+ self.writeln("" + allowUnencryptedNullCipher + "")
+
+ def writeInboundCertificateValidation(self, nap):
+ inboundCertificateValidation = nap.getInboundCertificateValidation()
+ if inboundCertificateValidation is not None and len(inboundCertificateValidation) > 0:
+ self.writeln("" + inboundCertificateValidation + "")
+
+ def writeOutboundCertificateValidation(self, nap):
+ outboundCertificateValidation = nap.getOutboundCertificateValidation()
+ if outboundCertificateValidation is not None and len(outboundCertificateValidation) > 0:
+ self.writeln("" + outboundCertificateValidation + "")
+
+ def createNameForLocalHostNetworkAccessPoint(self, nap_name, nap_name_dict):
+ # NAP names can be a maximum of 15 characters in length
+ key = nap_name
+ idx = 1
+ if len(nap_name) >= 10:
+ # Slice out the first 10 characters to use since there is a 15 character
+ # limit to NAP names
+ key = nap_name[:10]
+ if key not in nap_name_dict:
+ # Add the first nap name with index=1 into the Dictionary
+ nap_name_dict[key] = idx
+ else:
+ # Found a nap with the same first 10 characters so increment and
+ # save the index
+ idx = nap_name_dict[key] + 1
+ nap_name_dict[key] = idx
+
+ # zero fill to the left for single digit index (e.g. 01)
+ idx_str = str(idx)
+ if idx < 10:
+ idx_str = '0' + idx_str
+
+ # NAP name of for localhost binding will be of the form 'xxxxxxxxxx-lh01'
+ return key + WLS_LOCALHOST_IDENTIFIER + idx_str
+
+
+
def customizeServer(self, server):
name=server.getName()
listen_address=self.env.toDNS1123Legal(self.env.getDomainUID() + "-" + name)
@@ -1097,8 +1316,10 @@ def customizeServerTemplate(self, template):
self.writeln("")
def customizeNetworkAccessPoints(self, server, listen_address):
+ nap_name_dict = {}
for nap in server.getNetworkAccessPoints():
self.customizeNetworkAccessPoint(nap,listen_address)
+ self.createLocalHostNetworkAccessPoint(nap, '127.0.0.1', listen_address, nap_name_dict)
def customizeNetworkAccessPoint(self, nap, listen_address):
# Don't bother 'add' a nap listen-address, only do a 'replace'.
@@ -1110,18 +1331,86 @@ def customizeNetworkAccessPoint(self, nap, listen_address):
istio_enabled = self.env.getEnvOrDef("ISTIO_ENABLED", "false")
nap_name=nap.getName()
+ if nap_name in ISTIO_NAP_NAMES or nameContainsLocalHostIdentifier(nap_name):
+ # skip customizing internal channels that were generated
+ return
+
+ # replace listen address to bind to server pod IP
if not (nap.getListenAddress() is None) and len(nap.getListenAddress()) > 0:
self.writeln("")
self.indent()
self.writeln("" + nap_name + "")
- if istio_enabled == 'true':
- self.writeListenAddress("force a replace", '127.0.0.1')
- else:
- self.writeListenAddress("force a replace",listen_address)
-
+ self.writeListenAddress("force a replace",listen_address)
self.undent()
self.writeln("")
+ # Create copy of custom NAP for binding to localhost for handling k8s 'port-forward'
+ # feature and Istio versions < 1.10.x
+ def createLocalHostNetworkAccessPoint(self, nap, listen_address, public_listen_address, nap_name_dict):
+ # Don't bother 'add' a nap listen-address, only do a 'replace'.
+ # If we try 'add' this appears to mess up an attempt to
+ # 'add' PublicAddress/Port via custom sit-cfg.
+ # FWIW there's theoretically no need to 'add' or 'replace' when empty
+ # since the runtime default is the server listen-address.
+
+ istio_enabled = self.env.getEnvOrDef("ISTIO_ENABLED", "false")
+ if istio_enabled == 'true':
+ nap_name=nap.getName()
+ if nap_name in ISTIO_NAP_NAMES or nameContainsLocalHostIdentifier(nap_name):
+ # skip creating ISTIO channels
+ return
+
+ self.writeln('')
+ self.indent()
+ self.writeName(self.createNameForLocalHostNetworkAccessPoint(nap_name, nap_name_dict))
+ self.writeGeneralNetworkAccessPointConfiguration(nap, listen_address, public_listen_address)
+ self.writeSecureNetworkAccessPointConfiguration(nap)
+ self.undent()
+ self.writeln("")
+
+ def writeGeneralNetworkAccessPointConfiguration(self, nap, listen_address, public_listen_address):
+ self.writeProtocol(nap.getProtocol())
+ self.writeListenAddress("", listen_address)
+ self.writeListenPort(nap.getListenPort())
+ self.writePublicAddress(public_listen_address)
+ self.writePublicPort(nap.getPublicPort())
+ self.writeEnabled(nap)
+ self.writeOutboundEnabled(nap)
+ self.writeAcceptBacklog(nap.getAcceptBacklog())
+ self.writeMaxBackoffBetweenFailures(nap.getMaxBackoffBetweenFailures())
+ self.writeHttpEnabledForThisProtocol(nap)
+ self.writeLoginTimeoutMillis(nap.getLoginTimeoutMillis())
+ self.writeCompleteMessageTimeout(nap.getCompleteMessageTimeout())
+ self.writeIdleConnectionTimeout(nap.getIdleConnectionTimeout())
+ self.writeConnectTimeout(nap.getConnectTimeout())
+ self.writeTimeoutConnectionWithPendingResponses(nap)
+ self.writeTunnelingEnabled(nap)
+ self.writeTunnelingClientPingSecs(nap.getTunnelingClientPingSecs())
+ self.writeTunnelingClientTimeoutSecs(nap.getTunnelingClientTimeoutSecs())
+ self.writeMaxMessageSize(nap.getMaxMessageSize())
+ self.writeChannelWeight(nap.getChannelWeight())
+ self.writeMaxConnectedClients(nap.getMaxConnectedClients())
+ self.writeUseFastSerialization(nap)
+ self.writeSDPEnabled(nap)
+
+ def writeSecureNetworkAccessPointConfiguration(self, nap):
+ protocol = nap.getProtocol()
+ if protocol.endswith('s') or protocol == 'admin':
+ self.writeTwoWaySSLEnabled(nap)
+ self.writeClientCertificateEnforced(nap)
+ self.writeChannelIdentityCustomized(nap)
+ self.writeCustomPrivateKeyAlias(nap)
+ self.writeCustomPrivateKeyPassPhraseEncrypted(nap)
+ self.writeCustomIdentityKeyStoreType(nap)
+ self.writeCustomIdentityKeyStorePassPhraseEncrypted(nap)
+ self.writeHostnameVerificationIgnored(nap)
+ self.writeHostnameVerifier(nap)
+ self.writeCiphersuites(nap)
+ self.writeAllowUnencryptedNullCipher(nap)
+ self.writeInboundCertificateValidation(nap)
+ self.writeOutboundCertificateValidation(nap)
+
+
def _getNapConfigOverrideAction(self, svr, testname):
replace_action = 'f:combine-mode="replace"'
add_action = 'f:combine-mode="add"'
@@ -1155,7 +1444,11 @@ def _writeIstioNAP(self, name, server, listen_address, listen_port, protocol, ht
self.writeln('%s' % name)
self.writeln('%s' % (action, protocol))
- self.writeln('127.0.0.1' % action)
+ if name == 'http-probe':
+ self.writeln('%s.%s' % (action, listen_address,
+ self.env.getEnvOrDef("ISTIO_POD_NAMESPACE", "default")))
+ else:
+ self.writeln('127.0.0.1' % action)
self.writeln('%s.%s' % (action, listen_address,
self.env.getEnvOrDef("ISTIO_POD_NAMESPACE", "default")))
self.writeln('%s' % (action, listen_port))
@@ -1210,6 +1503,9 @@ def customizeServerIstioNetworkAccessPoint(self, listen_address, server):
self._writeIstioNAP(name='http-probe', server=server, listen_address=listen_address,
listen_port=istio_readiness_port, protocol='http', http_enabled="true")
+ self._writeIstioNAP(name=self.createNameForLocalHostNetworkAccessPoint('http-probe', {}), server=server, listen_address=listen_address,
+ listen_port=istio_readiness_port, protocol='http', http_enabled="true")
+
# Generate NAP for each protocols
self._writeIstioNAP(name='tcp-ldap', server=server, listen_address=listen_address,
listen_port=admin_server_port, protocol='ldap')
@@ -1264,6 +1560,9 @@ def customizeManagedIstioNetworkAccessPoint(self, listen_address, template):
self._writeIstioNAP(name='http-probe', server=template, listen_address=listen_address,
listen_port=istio_readiness_port, protocol='http')
+ self._writeIstioNAP(name=self.createNameForLocalHostNetworkAccessPoint('http-probe', {}), server=template, listen_address=listen_address,
+ listen_port=istio_readiness_port, protocol='http')
+
self._writeIstioNAP(name='tcp-default', server=template, listen_address=listen_address,
listen_port=listen_port, protocol='t3', http_enabled='false')
@@ -1876,6 +2175,9 @@ def get_server_template_listening_ports_from_configxml(config_xml):
return server_template_ssls, server_template_ports
+def nameContainsLocalHostIdentifier(name):
+ return name.find(WLS_LOCALHOST_IDENTIFIER) > -1
+
def main(env):
try:
# Needs to build the domain first
diff --git a/operator/src/main/resources/scripts/model_wdt_mii_filter.py b/operator/src/main/resources/scripts/model_wdt_mii_filter.py
index 87326c94238..28a604e5062 100644
--- a/operator/src/main/resources/scripts/model_wdt_mii_filter.py
+++ b/operator/src/main/resources/scripts/model_wdt_mii_filter.py
@@ -44,7 +44,7 @@
# configuration.
#
-
+import copy
import inspect
import os
import sys
@@ -55,12 +55,20 @@
sys.path.append(tmp_scriptdir)
env = None
+ISTIO_NAP_NAMES = ['tcp-cbt', 'tcp-ldap', 'tcp-iiop', 'tcp-snmp', 'http-default', 'tcp-default', 'https-secure', 'tls-ldaps', 'tls-default', 'tls-cbts', 'tls-iiops', 'https-admin']
+WLS_LOCALHOST_IDENTIFIER = '-lh'
+
class OfflineWlstEnv(object):
def open(self, model):
self.model = model
+
+ # Dictionary to track the count of naps that have names > 15 characters
+ # key = 10 char name, value = index count
+ #self.nap_name_dict = {}
+
# before doing anything, get each env var and verify it exists
self.DOMAIN_UID = self.getEnv('DOMAIN_UID')
@@ -370,7 +378,7 @@ def getSSLOrNone(server):
return server['SSL']
-def _writeIstioNAP(name, server, listen_address, listen_port, protocol, http_enabled="true"):
+def _writeIstioNAP(name, server, listen_address, listen_port, protocol, http_enabled="true", bind_to_localhost="true"):
if 'NetworkAccessPoint' not in server:
server['NetworkAccessPoint'] = {}
@@ -381,7 +389,10 @@ def _writeIstioNAP(name, server, listen_address, listen_port, protocol, http_ena
nap = naps[name]
nap['Protocol'] = protocol
- nap['ListenAddress'] = '127.0.0.1'
+ if bind_to_localhost == 'true':
+ nap['ListenAddress'] = '127.0.0.1'
+ else:
+ nap['ListenAddress'] = '%s.%s' % (listen_address, env.getEnvOrDef("ISTIO_POD_NAMESPACE", "default"))
nap['PublicAddress'] = '%s.%s' % (listen_address, env.getEnvOrDef("ISTIO_POD_NAMESPACE", "default"))
nap['ListenPort'] = listen_port
nap['HttpEnabledForThisProtocol'] = http_enabled
@@ -405,7 +416,12 @@ def customizeServerIstioNetworkAccessPoint(server, listen_address):
# readiness probe
_writeIstioNAP(name='http-probe', server=server, listen_address=listen_address,
- listen_port=istio_readiness_port, protocol='http', http_enabled="true")
+ listen_port=istio_readiness_port, protocol='http', http_enabled="true",
+ bind_to_localhost="false")
+
+ # readiness probe NAP binding to localhost
+ _writeIstioNAP(name=createNameForLocalHostNetworkAccessPoint('http-probe', {}), server=server, listen_address=listen_address,
+ listen_port=istio_readiness_port, protocol='http', http_enabled="true")
# Generate NAP for each protocols
_writeIstioNAP(name='tcp-ldap', server=server, listen_address=listen_address,
@@ -471,6 +487,11 @@ def customizeManagedIstioNetworkAccessPoint(template, listen_address):
listen_port = 7001
# readiness probe
_writeIstioNAP(name='http-probe', server=template, listen_address=listen_address,
+ listen_port=istio_readiness_port, protocol='http', http_enabled="true",
+ bind_to_localhost="false")
+
+ # readiness probe NAP binding to localhost address for Istio versions < 1.10.x
+ _writeIstioNAP(name=createNameForLocalHostNetworkAccessPoint('http-probe', {}), server=template, listen_address=listen_address,
listen_port=istio_readiness_port, protocol='http', http_enabled="true")
# Generate NAP for each protocols
@@ -525,22 +546,88 @@ def customizeNetworkAccessPoints(server, listen_address):
naps = server['NetworkAccessPoint']
nap_names = naps.keys()
+ # Dictionary to track the count of naps that have names > 15 characters
+ # key = 10 char name, value = index count
+ nap_name_dict = {}
+ # Dictionary of LocalHost NAP's created
+ local_nap_dict = {}
for nap_name in nap_names:
nap = naps[nap_name]
- customizeNetworkAccessPoint(nap, listen_address)
+ customizeNetworkAccessPoint(nap_name, nap, listen_address)
+ createLocalHostNetworkAccessPoint(nap_name, nap, nap_name_dict, local_nap_dict)
+ # Iterate through the Dictionary of cloned local NAP's and add to the NetworkAccesPoint
+ # list of the model
+ local_nap_names = local_nap_dict.keys()
+ for local_nap_name in local_nap_names:
+ server['NetworkAccessPoint'][local_nap_name] = local_nap_dict[local_nap_name]
-def customizeNetworkAccessPoint(nap, listen_address):
- istio_enabled = env.getEnvOrDef("ISTIO_ENABLED", "false")
+def customizeNetworkAccessPoint(nap_name, nap, listen_address):
+ if nap_name in ISTIO_NAP_NAMES or nameContainsLocalHostIdentifier(nap_name):
+ # skip creating ISTIO channels
+ return
+
+ # fix NAP listen address
if 'ListenAddress' in nap:
original_listen_address = nap['ListenAddress']
if len(original_listen_address) > 0:
- if istio_enabled == 'true':
- nap['ListenAddress'] = '127.0.0.1'
- else:
nap['ListenAddress'] = listen_address
+# Create copy of custom NAP for binding to localhost for handling k8s 'port-forward'
+# feature and Istio versions < 1.10.x
+def createLocalHostNetworkAccessPoint(nap_name, nap, nap_name_dict, local_nap_dict):
+ istio_enabled = env.getEnvOrDef("ISTIO_ENABLED", "false")
+ if istio_enabled == 'true':
+ if nap_name in ISTIO_NAP_NAMES or nameContainsLocalHostIdentifier(nap_name):
+ # skip creating ISTIO channels
+ return
+
+ wls_local_nap = copy.deepcopy(nap)
+ wls_local_nap['ListenAddress'] = '127.0.0.1'
+ local_nap_name = createNameForLocalHostNetworkAccessPoint(nap_name, nap_name_dict)
+ local_nap_dict[local_nap_name] = wls_local_nap
+
+def createNameForLocalHostNetworkAccessPoint(nap_name, nap_name_dict):
+ # NAP names can be a maximum of 15 characters in length
+ key = nap_name
+ idx = 1
+ if len(nap_name) >= 10:
+ # Slice out the first 10 characters to use since there is a 15 character
+ # limit to NAP names
+ key = nap_name[:10]
+ if key not in nap_name_dict:
+ # Add the first nap name with index=1 into the Dictionary
+ nap_name_dict[key] = idx
+ else:
+ # Found a nap with the same first 10 characters so increment and
+ # save the index
+ idx = nap_name_dict[key] + 1
+ nap_name_dict[key] = idx
+
+ # zero fill to the left for single digit index (e.g. 01)
+ idx_str = str(idx)
+ if idx < 10:
+ idx_str = '0' + idx_str
+
+ # NAP name of for localhost binding will be of the form 'xxxxxxxxxx-lh01'
+ return key + WLS_LOCALHOST_IDENTIFIER + idx_str
+
+def nameContainsLocalHostIdentifier(name):
+ # look for '-lh'
+ identifierIdx = name.find(WLS_LOCALHOST_IDENTIFIER)
+ # check if there is a localhost identifier
+ if identifierIdx > -1:
+ endIdentifierIdx = identifierIdx + len(WLS_LOCALHOST_IDENTIFIER)
+ # get substring from localhost identifier to end
+ subStr = name[endIdentifierIdx:]
+ # should be only 2 digits 'NN' from '-lhNN' format
+ if len(subStr) == 2:
+ # verify the last two chars are digits
+ if subStr.isdigit():
+ return True
+
+ return False
def setServerListenAddress(serverOrTemplate, listen_address):
serverOrTemplate['ListenAddress'] = listen_address
diff --git a/operator/src/test/python/test_wdt_mii_filter.py b/operator/src/test/python/test_wdt_mii_filter.py
index ccca77c2355..bcc9e697617 100644
--- a/operator/src/test/python/test_wdt_mii_filter.py
+++ b/operator/src/test/python/test_wdt_mii_filter.py
@@ -8,7 +8,7 @@
class WdtUpdateFilterCase(unittest.TestCase):
- ISTIO_NAP_NAMES = ['tcp-cbt', 'tcp-ldap', 'tcp-iiop', 'tcp-snmp', 'http-probe', 'http-default', 'tcp-default']
+ ISTIO_NAP_NAMES = ['tcp-cbt', 'tcp-ldap', 'tcp-iiop', 'tcp-snmp', 'http-probe', 'http-probe' + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '01', 'http-default', 'tcp-default']
def setUp(self):
self.initialize_environment_variables()
@@ -147,7 +147,17 @@ def test_customize_istio_enabled_network_access_points_in_server_template(self):
server_template = self.getServerTemplate(model)
model_wdt_mii_filter.customizeNetworkAccessPoints(server_template, 'sample-domain1-managed-server${id}')
nap_listen_address = model['topology']['ServerTemplate']['cluster-1-template']['NetworkAccessPoint']['T3Channel']['ListenAddress']
- self.assertEqual('127.0.0.1', nap_listen_address, "Expected nap listen address to be \'127.0.0.1\'")
+ self.assertEqual('sample-domain1-managed-server${id}', nap_listen_address, "Expected nap listen address to be \'sample-domain1-managed-server${id}\'")
+
+ # Assert LocalHost channel listen address is '127.0.0.1'
+ nap_local_host = model['topology']['ServerTemplate']['cluster-1-template']['NetworkAccessPoint']['T3Channel' + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '01']['ListenAddress']
+ self.assertEqual('127.0.0.1', nap_local_host, "Expected nap listen address to be \'127.0.0.1\'")
+
+ # Verify we don't duplicate channels more than once
+ model_wdt_mii_filter.customizeNetworkAccessPoints(server_template, 'sample-domain1-managed-server${id}')
+ naps = server_template['NetworkAccessPoint']
+ nap_names = naps.keys()
+ self.assertEqual(2, len(nap_names), "Expected only two NAPS")
finally:
del os.environ['ISTIO_ENABLED']
@@ -236,6 +246,86 @@ def test_readDomainNameFromTopologyYaml(self):
domain_name = model_wdt_mii_filter.env.getDomainName()
self.assertEqual('wls-domain1', domain_name, "Expected domain name to be \'wls-domain1\'")
+ def test_createNameForLocalHostNetworkAccessPoint(self):
+ model = self.getModel()
+ nap_name_copy = model_wdt_mii_filter.createNameForLocalHostNetworkAccessPoint('http-probe', {})
+ self.assertEqual('http-probe' + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '01', nap_name_copy, "Expected name to be http-probe" + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '01')
+
+ def test_createNameForLocalHostNetworkAccessPoint_with_long_name(self):
+ nap_name_10_chars = 'abcdefghij'
+ nap_name_15_chars = nap_name_10_chars + 'klmno'
+ nap_name_20_chars = nap_name_15_chars + 'pqrst'
+
+ model = self.getModel()
+ nap_name_dict = {}
+ nap_name_copy = model_wdt_mii_filter.createNameForLocalHostNetworkAccessPoint(nap_name_10_chars, nap_name_dict)
+ self.assertEqual(nap_name_10_chars + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '01', nap_name_copy, "Expected name to be " + nap_name_10_chars + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '01')
+
+ nap_name_copy = model_wdt_mii_filter.createNameForLocalHostNetworkAccessPoint(nap_name_15_chars, nap_name_dict)
+ self.assertEqual(nap_name_10_chars + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '02', nap_name_copy, "Expected 15 character name to be " + nap_name_10_chars + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '02')
+
+ nap_name_copy = model_wdt_mii_filter.createNameForLocalHostNetworkAccessPoint(nap_name_20_chars, nap_name_dict)
+ self.assertEqual(nap_name_10_chars + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '03', nap_name_copy, "Expected name to be " + nap_name_10_chars + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '03')
+
+ def test_createNameForLocalHostNetworkAccessPoint_with_double_digit_idx(self):
+ nap_name_10_chars = 'abcdefghij'
+ nap_name_20_chars = nap_name_10_chars + 'klmnopqrst'
+ nap_name_dict = {}
+
+ # 01
+ nap_name_copy = model_wdt_mii_filter.createNameForLocalHostNetworkAccessPoint(nap_name_20_chars, nap_name_dict)
+ self.assertEqual(nap_name_10_chars + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '01', nap_name_copy, "Expected name to be " + nap_name_10_chars + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '01')
+
+ # 02
+ nap_name_copy = model_wdt_mii_filter.createNameForLocalHostNetworkAccessPoint(nap_name_20_chars, nap_name_dict)
+ self.assertEqual(nap_name_10_chars + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '02', nap_name_copy, "Expected name to be " + nap_name_10_chars + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '01')
+
+ model_wdt_mii_filter.createNameForLocalHostNetworkAccessPoint(nap_name_20_chars, nap_name_dict)
+ model_wdt_mii_filter.createNameForLocalHostNetworkAccessPoint(nap_name_20_chars, nap_name_dict)
+ model_wdt_mii_filter.createNameForLocalHostNetworkAccessPoint(nap_name_20_chars, nap_name_dict)
+ model_wdt_mii_filter.createNameForLocalHostNetworkAccessPoint(nap_name_20_chars, nap_name_dict)
+ model_wdt_mii_filter.createNameForLocalHostNetworkAccessPoint(nap_name_20_chars, nap_name_dict)
+ model_wdt_mii_filter.createNameForLocalHostNetworkAccessPoint(nap_name_20_chars, nap_name_dict)
+ model_wdt_mii_filter.createNameForLocalHostNetworkAccessPoint(nap_name_20_chars, nap_name_dict)
+ # 10
+ nap_name_copy = model_wdt_mii_filter.createNameForLocalHostNetworkAccessPoint(nap_name_20_chars, nap_name_dict)
+ self.assertEqual(nap_name_10_chars + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '10', nap_name_copy, "Expected name to be " + nap_name_10_chars + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '10')
+
+ def test_createLocalHostNetworkAccessPoint(self):
+ try:
+ os.environ['ISTIO_ENABLED'] = 'true'
+ model = self.getModel()
+ server_template = self.getServerTemplate(model)
+ naps = server_template['NetworkAccessPoint']
+
+ nap_name_dict = {}
+ local_nap_dict = {}
+ nap = naps['T3Channel']
+ model_wdt_mii_filter.createLocalHostNetworkAccessPoint('T3Channel',
+ nap, nap_name_dict, local_nap_dict)
+ self.assertEqual(1, len(local_nap_dict), "Expected only one dictionary entry")
+ nap = local_nap_dict['T3Channel' +
+ model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '01']
+ self.assertEqual('127.0.0.1', nap['ListenAddress'],
+ "Expected ListenAddress to be '\127.0.0.1\'")
+ finally:
+ del os.environ['ISTIO_ENABLED']
+
+ def test_nameContainsLocalHostIdentifier(self):
+ channelName = 'abced' + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + '01'
+ self.assertTrue(model_wdt_mii_filter.nameContainsLocalHostIdentifier(channelName),
+ "Expected name to contain " + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER)
+
+ def test_nameDoesNotContainsLocalHostIdentifier(self):
+ channelName = 'abcedefg-l01'
+ self.assertFalse(model_wdt_mii_filter.nameContainsLocalHostIdentifier(channelName),
+ "Expected name to not contain " + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER)
+
+ def test_localHostIdentifierDoesNotContainDigits(self):
+ channelName = 'abced' + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER + 'o1'
+ self.assertFalse(model_wdt_mii_filter.nameContainsLocalHostIdentifier(channelName),
+ "Expected name to not contain " + model_wdt_mii_filter.WLS_LOCALHOST_IDENTIFIER)
+
class MockOfflineWlstEnv(model_wdt_mii_filter.OfflineWlstEnv):
WLS_CRED_USERNAME = 'weblogic'