You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@apache.org on 2018/10/08 14:09:16 UTC
svn commit: r1843148 - in /tomcat/trunk: ./
java/org/apache/catalina/tribes/membership/cloud/ res/checkstyle/
webapps/docs/
Author: remm
Date: Mon Oct 8 14:09:16 2018
New Revision: 1843148
URL: http://svn.apache.org/viewvc?rev=1843148&view=rev
Log:
- Experimental Kubernetes aware cloud membership provider, based on code by Maxime Beck.
- Contains code derived from jgroups.
- Documentation is hard and will be a wip.
- Requires openjson to run.
- Kube is usually available so it should provide wide support, but native support for certain other orchestrators like Docker is a possibility.
Added:
tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/
tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/AbstractStreamProvider.java (with props)
tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CertificateStreamProvider.java (with props)
tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java (with props)
tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java (with props)
tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/Constants.java (with props)
tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/InsecureStreamProvider.java (with props)
tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java (with props)
tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties (with props)
tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/StreamProvider.java (with props)
tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java (with props)
Modified:
tomcat/trunk/NOTICE
tomcat/trunk/build.properties.default
tomcat/trunk/build.xml
tomcat/trunk/res/checkstyle/org-import-control.xml
tomcat/trunk/webapps/docs/changelog.xml
Modified: tomcat/trunk/NOTICE
URL: http://svn.apache.org/viewvc/tomcat/trunk/NOTICE?rev=1843148&r1=1843147&r2=1843148&view=diff
==============================================================================
--- tomcat/trunk/NOTICE (original)
+++ tomcat/trunk/NOTICE Mon Oct 8 14:09:16 2018
@@ -28,6 +28,12 @@ project developed at Twitter
* Copyright 2014 The Netty Project
* Copyright 2014 Twitter
+For portions of the Tomcat cloud support
+The org.apache.catalina.tribes.membership.cloud package contains derivative
+work originating from the jgroups project.
+https://github.com/jgroups-extras/jgroups-kubernetes
+Copyright 2018 Red Hat Inc.
+
The original XML Schemas for Java EE Deployment Descriptors:
- javaee_5.xsd
- javaee_web_services_1_2.xsd
Modified: tomcat/trunk/build.properties.default
URL: http://svn.apache.org/viewvc/tomcat/trunk/build.properties.default?rev=1843148&r1=1843147&r2=1843148&view=diff
==============================================================================
--- tomcat/trunk/build.properties.default (original)
+++ tomcat/trunk/build.properties.default Mon Oct 8 14:09:16 2018
@@ -283,6 +283,15 @@ saaj-api.home=${base.path}/saaj-api-${sa
saaj-api.jar=${saaj-api.home}/saaj-api-${saaj-api.version}.jar
saaj-api.loc=${base-maven.loc}/javax/xml/soap/saaj-api/${saaj-api.version}/saaj-api-${saaj-api.version}.jar
+# ----- OpenJSON, version 1.0.10 or later -----
+openjson.version=1.0.10
+openjson.checksum.enabled=true
+openjson.checksum.algorithm=MD5|SHA-1
+openjson.checksum.value=c7c4cb9266cacc0aab5dcbb59456720c|8dcccbcc8bbfa15162cd7ca77bcf2b9daa90e70a
+openjson.home=${base.path}/openjson-${openjson.version}
+openjson.jar=${easymock.home}/openjson-${openjson.version}.jar
+openjson.loc=${base-maven.loc}/com/github/openjson/openjson/${openjson.version}/openjson-${openjson.version}.jar
+
# ----- bnd & bndlib, version 4.0.0 or later -----
# ----- provides OSGI metadata for JARs -----
bnd.version=4.0.0
Modified: tomcat/trunk/build.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/build.xml?rev=1843148&r1=1843147&r2=1843148&view=diff
==============================================================================
--- tomcat/trunk/build.xml (original)
+++ tomcat/trunk/build.xml Mon Oct 8 14:09:16 2018
@@ -213,6 +213,7 @@
<path id="compile.classpath">
<pathelement location="${jdt.jar}"/>
<pathelement location="${saaj-api.jar}"/>
+ <pathelement location="${openjson.jar}"/>
</path>
<path id="tomcat.classpath">
@@ -2717,7 +2718,17 @@ skip.installer property in build.propert
<param name="checksum.value" value="${saaj-api.checksum.value}"/>
</antcall>
- </target>
+ <!-- Download openjson -->
+ <antcall target="downloadfile">
+ <param name="sourcefile" value="${openjson.loc}"/>
+ <param name="destfile" value="${openjson.jar}"/>
+ <param name="destdir" value="${openjson.home}"/>
+ <param name="checksum.enabled" value="${openjson.checksum.enabled}"/>
+ <param name="checksum.algorithm" value="${openjson.checksum.algorithm}"/>
+ <param name="checksum.value" value="${openjson.checksum.value}"/>
+ </antcall>
+
+ </target>
<target name="download-test-compile"
description="Download additional components for the tests" >
Added: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/AbstractStreamProvider.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/AbstractStreamProvider.java?rev=1843148&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/AbstractStreamProvider.java (added)
+++ tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/AbstractStreamProvider.java Mon Oct 8 14:09:16 2018
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.tribes.membership.cloud;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import org.apache.catalina.tribes.membership.Constants;
+import org.apache.catalina.tribes.util.StringManager;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+public abstract class AbstractStreamProvider implements StreamProvider {
+ private static final Log log = LogFactory.getLog(AbstractStreamProvider.class);
+ protected static final StringManager sm = StringManager.getManager(Constants.Package);
+
+ protected static final TrustManager[] INSECURE_TRUST_MANAGERS = new TrustManager[] {
+ new X509TrustManager() {
+ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
+ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
+ public X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ }
+ };
+
+ /**
+ * Open URL connection to the specified URL.
+ * @param url the url
+ * @param headers the headers map
+ * @param connectTimeout connection timeout in ms
+ * @param readTimeout read timeout in ms
+ * @return the URL connection
+ * @throws IOException when an error occurs
+ */
+ public URLConnection openConnection(String url, Map<String, String> headers, int connectTimeout, int readTimeout) throws IOException {
+ if (log.isDebugEnabled()) {
+ log.debug(String.format("%s opening connection: url [%s], headers [%s], connectTimeout [%s], readTimeout [%s]", getClass().getSimpleName(), url, headers, connectTimeout, readTimeout));
+ }
+ URLConnection connection = new URL(url).openConnection();
+ if (headers != null) {
+ for (Map.Entry<String, String> entry : headers.entrySet()) {
+ connection.addRequestProperty(entry.getKey(), entry.getValue());
+ }
+ }
+ if (connectTimeout < 0 || readTimeout < 0) {
+ throw new IllegalArgumentException(
+ String.format("Neither connectTimeout [%s] nor readTimeout [%s] can be less than 0 for URLConnection.", connectTimeout, readTimeout));
+ }
+ connection.setConnectTimeout(connectTimeout);
+ connection.setReadTimeout(readTimeout);
+ return connection;
+ }
+
+}
Propchange: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/AbstractStreamProvider.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CertificateStreamProvider.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CertificateStreamProvider.java?rev=1843148&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CertificateStreamProvider.java (added)
+++ tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CertificateStreamProvider.java Mon Oct 8 14:09:16 2018
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.tribes.membership.cloud;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLConnection;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.net.jsse.PEMFile;
+
+public class CertificateStreamProvider extends AbstractStreamProvider {
+
+ private static final Log log = LogFactory.getLog(CertificateStreamProvider.class);
+
+ private final SSLSocketFactory factory;
+
+ CertificateStreamProvider(String clientCertFile, String clientKeyFile, String clientKeyPassword, String clientKeyAlgo, String caCertFile) throws Exception {
+ // defaults - RSA and empty password
+ char[] password = (clientKeyPassword != null) ? clientKeyPassword.toCharArray() : new char[0];
+ String algorithm = (clientKeyAlgo != null) ? clientKeyAlgo : "RSA";
+
+ KeyManager[] keyManagers = configureClientCert(clientCertFile, clientKeyFile, password, algorithm);
+ TrustManager[] trustManagers = configureCaCert(caCertFile);
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(keyManagers, trustManagers, null);
+ factory = context.getSocketFactory();
+ }
+
+ @Override
+ public InputStream openStream(String url, Map<String, String> headers, int connectTimeout, int readTimeout) throws IOException {
+ URLConnection connection = openConnection(url, headers, connectTimeout, readTimeout);
+ if (connection instanceof HttpsURLConnection) {
+ HttpsURLConnection httpsConnection = HttpsURLConnection.class.cast(connection);
+ //httpsConnection.setHostnameVerifier(InsecureStreamProvider.INSECURE_HOSTNAME_VERIFIER);
+ httpsConnection.setSSLSocketFactory(factory);
+ if (log.isDebugEnabled()) {
+ log.debug(String.format("Using HttpsURLConnection with SSLSocketFactory [%s] for url [%s].", factory, url));
+ }
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug(String.format("Using URLConnection for url [%s].", url));
+ }
+ }
+ return connection.getInputStream();
+ }
+
+ private static KeyManager[] configureClientCert(String clientCertFile, String clientKeyFile, char[] clientKeyPassword, String clientKeyAlgo) throws Exception {
+ try (InputStream certInputStream = new FileInputStream(clientCertFile)) {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X509");
+ X509Certificate cert = (X509Certificate)certFactory.generateCertificate(certInputStream);
+
+ PEMFile pemFile = new PEMFile(clientKeyFile, new String(clientKeyPassword));
+ PrivateKey privKey = pemFile.getPrivateKey();
+
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null, null);
+
+ String alias = cert.getSubjectX500Principal().getName();
+ keyStore.setKeyEntry(alias, privKey, clientKeyPassword, new Certificate[]{cert});
+
+ KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ keyManagerFactory.init(keyStore, clientKeyPassword);
+
+ return keyManagerFactory.getKeyManagers();
+ } catch (IOException e) {
+ log.error(sm.getString("certificateStream.clientCertError", clientCertFile, clientKeyFile), e);
+ throw e;
+ }
+ }
+
+ private static TrustManager[] configureCaCert(String caCertFile) throws Exception {
+ if (caCertFile != null) {
+ try (InputStream pemInputStream = new FileInputStream(caCertFile)) {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X509");
+ X509Certificate cert = (X509Certificate) certFactory.generateCertificate(pemInputStream);
+
+ KeyStore trustStore = KeyStore.getInstance("JKS");
+ trustStore.load(null);
+
+ String alias = cert.getSubjectX500Principal().getName();
+ trustStore.setCertificateEntry(alias, cert);
+
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init(trustStore);
+
+ return trustManagerFactory.getTrustManagers();
+ } catch (Exception e) {
+ log.error(sm.getString("certificateStream.CACertError", caCertFile), e);
+ throw e;
+ }
+ } else {
+ log.warn(sm.getString("certificateStream.CACertUndefined"));
+ return InsecureStreamProvider.INSECURE_TRUST_MANAGERS;
+ }
+ }
+
+}
Propchange: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CertificateStreamProvider.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java?rev=1843148&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java (added)
+++ tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java Mon Oct 8 14:09:16 2018
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.tribes.membership.cloud;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.InetAddress;
+import java.security.AccessController;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivilegedAction;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.catalina.tribes.ChannelListener;
+import org.apache.catalina.tribes.Heartbeat;
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.membership.Constants;
+import org.apache.catalina.tribes.membership.Membership;
+import org.apache.catalina.tribes.membership.MembershipProviderBase;
+import org.apache.catalina.tribes.util.StringManager;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+public abstract class CloudMembershipProvider extends MembershipProviderBase implements Heartbeat, ChannelListener {
+ private static final Log log = LogFactory.getLog(KubernetesMembershipProvider.class);
+ protected static final StringManager sm = StringManager.getManager(Constants.Package);
+
+ protected String url;
+ protected StreamProvider streamProvider;
+ protected int connectionTimeout;
+ protected int readTimeout;
+
+ protected Instant startTime;
+ protected MessageDigest md5;
+
+ protected Map<String, String> headers = new HashMap<>();
+
+ protected int port;
+ protected String hostName;
+
+ public CloudMembershipProvider() {
+ try {
+ md5 = MessageDigest.getInstance("md5");
+ } catch (NoSuchAlgorithmException e) {
+ // Ignore
+ }
+ }
+
+ // Get value of environment variable named keys[0]
+ // If keys[0] isn't found, try keys[1], keys[2], ...
+ // If nothing is found, return null
+ protected static String getEnv(String... keys) {
+ String val = null;
+
+ for (String key : keys) {
+ val = AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getenv(key));
+ if (val != null)
+ break;
+ }
+
+ return val;
+ }
+
+ @Override
+ public void init(Properties properties) throws IOException {
+ startTime = Instant.now();
+
+ connectionTimeout = Integer.parseInt(properties.getProperty("connectionTimeout", "1000"));
+ readTimeout = Integer.parseInt(properties.getProperty("readTimeout", "1000"));
+
+ hostName = InetAddress.getLocalHost().getHostName();
+ port = Integer.parseInt(properties.getProperty("tcpListenPort"));
+ }
+
+ @Override
+ public void start(int level) throws Exception {
+ if (membership == null) {
+ membership = new Membership(service.getLocalMember(true));
+ }
+ service.getChannel().addChannelListener(this);
+ }
+
+ @Override
+ public boolean stop(int level) throws Exception {
+ return true;
+ }
+
+ @Override
+ public void heartbeat() {
+ log.debug("Fetching announced members");
+ Member[] announcedMembers = fetchMembers();
+ // Add new members or refresh the members in the membership
+ for (Member member : announcedMembers) {
+ if (membership.memberAlive(member)) {
+ membershipListener.memberAdded(member);
+ }
+ }
+ // Remove non refreshed members from the membership
+ Member[] expired = membership.expire(100); // TODO: is 100ms a good value?
+ for (Member member : expired) {
+ if (log.isDebugEnabled()) {
+ log.debug("Member is dead: " + member);
+ }
+ membershipListener.memberDisappeared(member);
+ }
+ }
+
+ /**
+ * Fetch current cluster members from the cloud orchestration.
+ * @return the member array
+ */
+ protected abstract Member[] fetchMembers();
+
+ @Override
+ public void messageReceived(Serializable msg, Member sender) {
+ }
+
+ @Override
+ public boolean accept(Serializable msg, Member sender) {
+ return false;
+ }
+
+}
Propchange: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipProvider.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java?rev=1843148&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java (added)
+++ tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java Mon Oct 8 14:09:16 2018
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.tribes.membership.cloud;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+import javax.management.ObjectName;
+
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.MembershipProvider;
+import org.apache.catalina.tribes.MembershipService;
+import org.apache.catalina.tribes.jmx.JmxRegistry;
+import org.apache.catalina.tribes.membership.Constants;
+import org.apache.catalina.tribes.membership.MemberImpl;
+import org.apache.catalina.tribes.membership.MembershipServiceBase;
+import org.apache.catalina.tribes.util.StringManager;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+public class CloudMembershipService extends MembershipServiceBase {
+ private static final Log log = LogFactory.getLog(CloudMembershipService.class);
+ protected static final StringManager sm = StringManager.getManager(Constants.Package);
+
+ public static final String MEMBERSHIP_PROVIDER_CLASS_NAME = "membershipProviderClassName";
+ private static final String KUBE = "kubernetes";
+ private static final String KUBE_PROVIDER_CLASS = "org.apache.catalina.cloud.membership.KubernetesMembershipProvider";
+
+ private MembershipProvider membershipProvider;
+ private MemberImpl localMember;
+
+ private byte[] payload;
+ private byte[] domain;
+
+ private ObjectName oname = null;
+
+ /**
+ * Return a property.
+ * @param name the property name
+ * @return the property value
+ */
+ public Object getProperty(String name) {
+ return properties.getProperty(name);
+ }
+
+ /**
+ * Set a property.
+ * @param name the property name
+ * @param value the property value
+ * @return <code>true</code> if the property was successfully set
+ */
+ public boolean setProperty(String name, String value) {
+ return (properties.setProperty(name, value) == null);
+ }
+
+ /**
+ * Return the membership provider class.
+ * @return the classname
+ */
+ public String getMembershipProviderClassName() {
+ return properties.getProperty(MEMBERSHIP_PROVIDER_CLASS_NAME);
+ }
+
+ /**
+ * Set the membership provider class.
+ * @param membershipProviderClassName the class name
+ */
+ public void setMembershipProviderClassName(String membershipProviderClassName) {
+ properties.setProperty(MEMBERSHIP_PROVIDER_CLASS_NAME, membershipProviderClassName);
+ }
+
+ @Override
+ public void start(int level) throws Exception {
+ if ((level & MembershipService.MBR_RX) == 0) {
+ return;
+ }
+
+ createOrUpdateLocalMember();
+ localMember.setServiceStartTime(System.currentTimeMillis());
+ localMember.setMemberAliveTime(100);
+ localMember.setPayload(payload);
+ localMember.setDomain(domain);
+
+ if (membershipProvider == null) {
+ String provider = getMembershipProviderClassName();
+ if (provider == null || KUBE.equals(provider)) {
+ provider = KUBE_PROVIDER_CLASS;
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("Using membershipProvider: " + provider);
+ }
+ membershipProvider = (MembershipProvider) Class.forName(provider).newInstance();
+ membershipProvider.setMembershipListener(this);
+ membershipProvider.setMembershipService(this);
+ membershipProvider.init(properties);
+ }
+ membershipProvider.start(level);
+
+ JmxRegistry jmxRegistry = JmxRegistry.getRegistry(channel);
+ if (jmxRegistry != null) {
+ oname = jmxRegistry.registerJmx(",component=Membership", this);
+ }
+ }
+
+ @Override
+ public void stop(int level) {
+ try {
+ if (membershipProvider != null && membershipProvider.stop(level)) {
+ if (oname != null) {
+ JmxRegistry.getRegistry(channel).unregisterJmx(oname);
+ oname = null;
+ }
+ membershipProvider = null;
+ channel = null;
+ }
+ } catch (Exception e) {
+ log.error(sm.getString("cloudMembershipService.stopFail", Integer.valueOf(level)), e);
+ }
+ }
+
+ @Override
+ public Member getLocalMember(boolean incAliveTime) {
+ if (incAliveTime && localMember != null) {
+ localMember.setMemberAliveTime(System.currentTimeMillis() - localMember.getServiceStartTime());
+ }
+ return localMember;
+ }
+
+ @Override
+ public void setLocalMemberProperties(String listenHost, int listenPort, int securePort, int udpPort) {
+ if (log.isDebugEnabled()) {
+ log.debug(String.format("setLocalMemberProperties(%s, %d, %d, %d)", listenHost, listenPort, securePort, udpPort));
+ }
+ properties.setProperty("tcpListenHost", listenHost);
+ properties.setProperty("tcpListenPort", String.valueOf(listenPort));
+ properties.setProperty("udpListenPort", String.valueOf(udpPort));
+ properties.setProperty("tcpSecurePort", String.valueOf(securePort));
+
+ try {
+ createOrUpdateLocalMember();
+ localMember.setPayload(payload);
+ localMember.setDomain(domain);
+ localMember.getData(true, true);
+ } catch (IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ private void createOrUpdateLocalMember() throws IOException {
+ String host = properties.getProperty("tcpListenHost");
+ int port = Integer.parseInt(properties.getProperty("tcpListenPort"));
+ int securePort = Integer.parseInt(properties.getProperty("tcpSecurePort"));
+ int udpPort = Integer.parseInt(properties.getProperty("udpListenPort"));
+
+ if (localMember == null) {
+ localMember = new MemberImpl();
+ try {
+ // Set localMember unique ID to md5 hash of hostname
+ localMember.setUniqueId(MessageDigest.getInstance("md5")
+ .digest(InetAddress.getLocalHost().getHostName().getBytes()));
+ } catch (NoSuchAlgorithmException e) {
+ throw new IOException(e);
+ }
+ localMember.setLocal(true);
+ }
+ localMember.setHostname(host);
+ localMember.setPort(port);
+ localMember.setSecurePort(securePort);
+ localMember.setUdpPort(udpPort);
+ localMember.getData(true, true);
+ }
+
+ @Override
+ public void setPayload(byte[] payload) {
+ this.payload = payload;
+ if (localMember != null) {
+ localMember.setPayload(payload);
+ }
+ }
+
+ @Override
+ public void setDomain(byte[] domain) {
+ this.domain = domain;
+ if (localMember != null) {
+ localMember.setDomain(domain);
+ }
+ }
+
+ @Override
+ public MembershipProvider getMembershipProvider() {
+ return membershipProvider;
+ }
+
+ public void setMembershipProvider(MembershipProvider memberProvider) {
+ this.membershipProvider = memberProvider;
+ }
+
+}
Propchange: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/CloudMembershipService.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/Constants.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/Constants.java?rev=1843148&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/Constants.java (added)
+++ tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/Constants.java Mon Oct 8 14:09:16 2018
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.tribes.membership.cloud;
+
+public class Constants {
+ public static final String Package = "org.apache.catalina.tribes.membership.cloud";
+}
\ No newline at end of file
Propchange: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/Constants.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/InsecureStreamProvider.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/InsecureStreamProvider.java?rev=1843148&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/InsecureStreamProvider.java (added)
+++ tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/InsecureStreamProvider.java Mon Oct 8 14:09:16 2018
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.tribes.membership.cloud;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLConnection;
+import java.util.Map;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocketFactory;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+public class InsecureStreamProvider extends AbstractStreamProvider {
+ private static final Log log = LogFactory.getLog(InsecureStreamProvider.class);
+
+ private static final HostnameVerifier INSECURE_HOSTNAME_VERIFIER = new HostnameVerifier() {
+ public boolean verify(String arg0, SSLSession arg1) {
+ return true;
+ }
+ };
+
+ private final SSLSocketFactory factory;
+
+ InsecureStreamProvider() throws Exception {
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, INSECURE_TRUST_MANAGERS, null);
+ factory = context.getSocketFactory();
+ }
+
+ @Override
+ public InputStream openStream(String url, Map<String, String> headers, int connectTimeout, int readTimeout) throws IOException {
+ URLConnection connection = openConnection(url, headers, connectTimeout, readTimeout);
+ if (connection instanceof HttpsURLConnection) {
+ HttpsURLConnection httpsConnection = HttpsURLConnection.class.cast(connection);
+ httpsConnection.setHostnameVerifier(INSECURE_HOSTNAME_VERIFIER);
+ httpsConnection.setSSLSocketFactory(factory);
+ if (log.isDebugEnabled()) {
+ log.debug(String.format("Using HttpsURLConnection with SSLSocketFactory [%s] for url [%s].", factory, url));
+ }
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug(String.format("Using URLConnection for url [%s].", url));
+ }
+ }
+ return connection.getInputStream();
+ }
+
+}
\ No newline at end of file
Propchange: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/InsecureStreamProvider.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java?rev=1843148&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java (added)
+++ tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java Mon Oct 8 14:09:16 2018
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.tribes.membership.cloud;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URLEncoder;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.MembershipService;
+import org.apache.catalina.tribes.membership.MemberImpl;
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+import org.apache.tomcat.util.codec.binary.StringUtils;
+
+import com.github.openjson.JSONArray;
+import com.github.openjson.JSONException;
+import com.github.openjson.JSONObject;
+import com.github.openjson.JSONTokener;
+
+
+public class KubernetesMembershipProvider extends CloudMembershipProvider {
+ private static final Log log = LogFactory.getLog(KubernetesMembershipProvider.class);
+
+ private static final String CUSTOM_ENV_PREFIX = "OPENSHIFT_KUBE_PING_";
+
+ @Override
+ public void start(int level) throws Exception {
+ if ((level & MembershipService.MBR_RX) == 0) {
+ return;
+ }
+
+ super.start(level);
+
+ // Set up Kubernetes API parameters
+ String namespace = getEnv("KUBERNETES_NAMESPACE", CUSTOM_ENV_PREFIX + "NAMESPACE");
+ if (namespace == null || namespace.length() == 0)
+ throw new RuntimeException(sm.getString("kubernetesMembershipProvider.noNamespace"));
+
+ if (log.isDebugEnabled()) {
+ log.debug(String.format("Namespace [%s] set; clustering enabled", namespace));
+ }
+
+ String protocol = getEnv("KUBERNETES_MASTER_PROTOCOL", CUSTOM_ENV_PREFIX + "MASTER_PROTOCOL");
+ String masterHost = getEnv("KUBERNETES_SERVICE_HOST", CUSTOM_ENV_PREFIX + "MASTER_HOST");
+ String masterPort = getEnv("KUBERNETES_SERVICE_PORT", CUSTOM_ENV_PREFIX + "MASTER_PORT");
+
+ String clientCertificateFile = getEnv("KUBERNETES_CLIENT_CERTIFICATE_FILE", CUSTOM_ENV_PREFIX + "CLIENT_CERT_FILE");
+ String caCertFile = getEnv("KUBERNETES_CA_CERTIFICATE_FILE", CUSTOM_ENV_PREFIX + "CA_CERT_FILE");
+ if (caCertFile == null) {
+ caCertFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";
+ }
+
+ if (clientCertificateFile == null) {
+ if (protocol == null) {
+ protocol = "https";
+ }
+ String saTokenFile = getEnv("SA_TOKEN_FILE", CUSTOM_ENV_PREFIX + "SA_TOKEN_FILE");
+ if (saTokenFile == null) {
+ saTokenFile = "/var/run/secrets/kubernetes.io/serviceaccount/token";
+ }
+ byte[] bytes = Files.readAllBytes(FileSystems.getDefault().getPath(saTokenFile));
+ streamProvider = new TokenStreamProvider(StringUtils.newStringUsAscii(bytes), caCertFile);
+ } else {
+ if (protocol == null) {
+ protocol = "http";
+ }
+ String clientKeyFile = getEnv("KUBERNETES_CLIENT_KEY_FILE");
+ String clientKeyPassword = getEnv("KUBERNETES_CLIENT_KEY_PASSWORD");
+ String clientKeyAlgo = getEnv("KUBERNETES_CLIENT_KEY_ALGO");
+ if (clientKeyAlgo == null) {
+ clientKeyAlgo = "RSA";
+ }
+ streamProvider = new CertificateStreamProvider(clientCertificateFile, clientKeyFile, clientKeyPassword, clientKeyAlgo, caCertFile);
+ }
+
+ String ver = getEnv("KUBERNETES_API_VERSION", CUSTOM_ENV_PREFIX + "API_VERSION");
+ if (ver == null)
+ ver = "v1";
+
+ String labels = getEnv("KUBERNETES_LABELS", CUSTOM_ENV_PREFIX + "LABELS");
+
+ namespace = URLEncoder.encode(namespace, "UTF-8");
+ labels = labels == null ? null : URLEncoder.encode(labels, "UTF-8");
+
+ url = String.format("%s://%s:%s/api/%s/namespaces/%s/pods", protocol, masterHost, masterPort, ver, namespace);
+ if (labels != null && labels.length() > 0) {
+ url = url + "?labelSelector=" + labels;
+ }
+
+ // Fetch initial members
+ heartbeat();
+ }
+
+ @Override
+ public boolean stop(int level) throws Exception {
+ try {
+ return super.stop(level);
+ } finally {
+ streamProvider = null;
+ }
+ }
+
+ @Override
+ protected Member[] fetchMembers() {
+ if (streamProvider == null) {
+ return new Member[0];
+ }
+
+ List<MemberImpl> members = new ArrayList<>();
+
+ try (InputStream stream = streamProvider.openStream(url, headers, connectionTimeout, readTimeout)) {
+ JSONObject json = new JSONObject(new JSONTokener(new InputStreamReader(stream, "UTF-8")));
+
+ JSONArray items = json.getJSONArray("items");
+
+ for (int i = 0; i < items.length(); i++) {
+ String phase;
+ String ip;
+ String name;
+ Instant creationTime;
+
+ try {
+ JSONObject item = items.getJSONObject(i);
+ JSONObject status = item.getJSONObject("status");
+ phase = status.getString("phase");
+
+ // Ignore shutdown pods
+ if (!phase.equals("Running"))
+ continue;
+
+ ip = status.getString("podIP");
+
+ // Get name & start time
+ JSONObject metadata = item.getJSONObject("metadata");
+ name = metadata.getString("name");
+ String timestamp = metadata.getString("creationTimestamp");
+ creationTime = Instant.parse(timestamp);
+ } catch (JSONException e) {
+ log.warn(sm.getString("kubernetesMembershipProvider.jsonError"), e);
+ continue;
+ }
+
+ // We found ourselves, ignore
+ if (name.equals(hostName))
+ continue;
+
+ // id = md5(hostname)
+ byte[] id = md5.digest(name.getBytes());
+ long aliveTime = Duration.between(creationTime, startTime).getSeconds() * 1000; // aliveTime is in ms
+
+ MemberImpl member = null;
+ try {
+ member = new MemberImpl(ip, port, aliveTime);
+ } catch (IOException e) {
+ // Shouldn't happen:
+ // an exception is thrown if hostname can't be resolved to IP, but we already provide an IP
+ log.warn(sm.getString("kubernetesMembershipProvider.memberError"), e);
+ continue;
+ }
+
+ member.setUniqueId(id);
+ members.add(member);
+ }
+ } catch (IOException e) {
+ log.warn(sm.getString("kubernetesMembershipProvider.streamError"), e);
+ }
+
+ return members.toArray(new Member[0]);
+ }
+
+}
Propchange: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/KubernetesMembershipProvider.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties?rev=1843148&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties (added)
+++ tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties Mon Oct 8 14:09:16 2018
@@ -0,0 +1,30 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+certificateStream.clientCertError=Could not create key manager for {0} ({1})
+certificateStream.CACertError=Could not create trust store for {0}
+certificateStream.CACertUndefined=CA cert undefined
+
+tokenStream.failedConnection=Failed connection to {0} with token {1} and CA {2}
+tokenStream.fileNotFound=CA cert file {0} not found
+tokenStream.trustManagerError=Could not create trust manager for {0}
+tokenStream.CACertUndefined=CA cert file undefined
+
+cloudMembershipService.stopFail=Unable to stop the static membership service, level: [{0}]
+
+kubernetesMembershipProvider.noNamespace=Namespace not set
+kubernetesMembershipProvider.jsonError=JSON error
+kubernetesMembershipProvider.memberError=Error creating member
+kubernetesMembershipProvider.streamError=Failed to open stream
Propchange: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/LocalStrings.properties
------------------------------------------------------------------------------
svn:eol-style = native
Added: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/StreamProvider.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/StreamProvider.java?rev=1843148&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/StreamProvider.java (added)
+++ tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/StreamProvider.java Mon Oct 8 14:09:16 2018
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.tribes.membership.cloud;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+
+public interface StreamProvider {
+ /**
+ * Open stream to the specified URL.
+ * @param url the url
+ * @param headers the headers map
+ * @param connectTimeout connection timeout in ms
+ * @param readTimeout read timeout in ms
+ * @return the stream
+ * @throws IOException when an error occurs
+ */
+ public InputStream openStream(String url, Map<String, String> headers, int connectTimeout, int readTimeout) throws IOException;
+}
Propchange: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/StreamProvider.java
------------------------------------------------------------------------------
svn:eol-style = native
Added: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java?rev=1843148&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java (added)
+++ tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java Mon Oct 8 14:09:16 2018
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.catalina.tribes.membership.cloud;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLConnection;
+import java.security.KeyStore;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.juli.logging.Log;
+import org.apache.juli.logging.LogFactory;
+
+public class TokenStreamProvider extends AbstractStreamProvider {
+
+ private static final Log log = LogFactory.getLog(TokenStreamProvider.class);
+
+ private String token;
+ private String caCertFile;
+ private SSLSocketFactory factory;
+
+ TokenStreamProvider(String token, String caCertFile) {
+ this.token = token;
+ this.caCertFile = caCertFile;
+ }
+
+ @Override
+ public InputStream openStream(String url, Map<String, String> headers, int connectTimeout, int readTimeout)
+ throws IOException {
+ // Set token header
+ if (token != null) {
+ headers.put("Authorization", "Bearer " + token);
+ }
+
+ // Open HTTP connection
+ URLConnection connection = openConnection(url, headers, connectTimeout, readTimeout);
+
+ if (connection instanceof HttpsURLConnection) {
+ HttpsURLConnection httpsConnection = HttpsURLConnection.class.cast(connection);
+ //httpsConnection.setHostnameVerifier(InsecureStreamProvider.INSECURE_HOSTNAME_VERIFIER);
+ httpsConnection.setSSLSocketFactory(getSSLSocketFactory());
+ if (log.isDebugEnabled()) {
+ log.debug(String.format("Using HttpsURLConnection with SSLSocketFactory [%s] for url [%s].", factory, url));
+ }
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug(String.format("Using URLConnection for url [%s].", url));
+ }
+ }
+
+ try {
+ return connection.getInputStream();
+ } catch (IOException e) {
+ throw new IOException(sm.getString("tokenStream.failedConnection", url, token, caCertFile), e);
+ }
+ }
+
+ private TrustManager[] configureCaCert(String caCertFile) throws Exception {
+ if (caCertFile != null) {
+ try {
+ InputStream pemInputStream = new BufferedInputStream(new FileInputStream(caCertFile));
+ try {
+ CertificateFactory certFactory = CertificateFactory.getInstance("X509");
+ X509Certificate cert = (X509Certificate)certFactory.generateCertificate(pemInputStream);
+
+ KeyStore trustStore = KeyStore.getInstance("JKS");
+ trustStore.load(null);
+
+ String alias = cert.getSubjectX500Principal().getName();
+ trustStore.setCertificateEntry(alias, cert);
+
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init(trustStore);
+
+ return trustManagerFactory.getTrustManagers();
+ } finally {
+ pemInputStream.close();
+ }
+ } catch (FileNotFoundException fnfe) {
+ log.error(sm.getString("tokenStream.fileNotFound", caCertFile));
+ throw fnfe;
+ } catch (Exception e) {
+ log.error(sm.getString("tokenStream.trustManagerError", caCertFile), e);
+ throw e;
+ }
+ } else {
+ log.warn(sm.getString("tokenStream.CACertUndefined"));
+ return InsecureStreamProvider.INSECURE_TRUST_MANAGERS;
+ }
+ }
+
+ private SSLSocketFactory getSSLSocketFactory() throws IOException {
+ if(this.factory == null) {
+ synchronized(this) {
+ if(this.factory == null) {
+ try {
+ TrustManager[] trustManagers = configureCaCert(this.caCertFile);
+ SSLContext context = SSLContext.getInstance("TLS");
+ context.init(null, trustManagers, null);
+ this.factory = context.getSocketFactory();
+ } catch(Exception e) {
+ throw new IOException(e);
+ }
+ }
+ }
+ }
+ return this.factory;
+ }
+
+}
\ No newline at end of file
Propchange: tomcat/trunk/java/org/apache/catalina/tribes/membership/cloud/TokenStreamProvider.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: tomcat/trunk/res/checkstyle/org-import-control.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/res/checkstyle/org-import-control.xml?rev=1843148&r1=1843147&r2=1843148&view=diff
==============================================================================
--- tomcat/trunk/res/checkstyle/org-import-control.xml (original)
+++ tomcat/trunk/res/checkstyle/org-import-control.xml Mon Oct 8 14:09:16 2018
@@ -81,6 +81,13 @@
<disallow pkg="org.apache.naming"/>
<disallow pkg="org.apache.tomcat"/>
<allow pkg="org.apache.catalina.tribes"/>
+ <subpackage name="membership">
+ <subpackage name="cloud">
+ <allow class="org.apache.tomcat.util.codec.binary.StringUtils"/>
+ <allow class="org.apache.tomcat.util.net.jsse.PEMFile"/>
+ <allow pkg="com.github.openjson"/>
+ </subpackage>
+ </subpackage>
</subpackage>
</subpackage>
<subpackage name="coyote">
Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1843148&r1=1843147&r2=1843148&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Mon Oct 8 14:09:16 2018
@@ -181,6 +181,10 @@
Add <code>setMembershipService</code> method to the
<code>MembershipProvider</code>. (kfujino)
</add>
+ <add>
+ Experimental Kubernetes aware cloud membership provider, based on code
+ by Maxime Beck. Contains code derived from jgroups. (remm/kfujino)
+ </add>
</changelog>
</subsection>
<subsection name="Other">
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org