All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.pires.hazelcast.HazelcastDiscoveryController Maven / Gradle / Ivy

/**
 * Licensed 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 com.github.pires.hazelcast;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hazelcast.config.*;
import com.hazelcast.core.Hazelcast;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Controller;

import javax.net.ssl.*;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Read from Kubernetes API all Hazelcast service bound pods, get their IP and connect to them.
 */
@Controller
public class HazelcastDiscoveryController implements CommandLineRunner {

    private static final Logger log = LoggerFactory.getLogger(HazelcastDiscoveryController.class);

    @JsonIgnoreProperties(ignoreUnknown = true)
    static class Address {
        public String ip;
    }

    @JsonIgnoreProperties(ignoreUnknown = true)
    static class Subset {
        public List
addresses; public List
notReadyAddresses; } @JsonIgnoreProperties(ignoreUnknown = true) static class Endpoints { public List subsets; } private static String getServiceAccountToken() throws IOException { String file = "/var/run/secrets/kubernetes.io/serviceaccount/token"; return new String(Files.readAllBytes(Paths.get(file))); } private static String getEnvOrDefault(String var, String def) { final String val = System.getenv(var); return (val == null || val.isEmpty()) ? def : val; } // TODO: Load the CA cert when it is available on all platforms. private static TrustManager[] trustAll = new TrustManager[]{ new X509TrustManager() { public void checkServerTrusted(X509Certificate[] certs, String authType) { } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public X509Certificate[] getAcceptedIssuers() { return null; } } }; private static HostnameVerifier trustAllHosts = new HostnameVerifier() { public boolean verify(String hostname, SSLSession session) { return true; } }; @Override public void run(String... args) { final String serviceName = getEnvOrDefault("HAZELCAST_SERVICE", "hazelcast"); final String namespace = getEnvOrDefault("POD_NAMESPACE", "default"); final String path = String.format("/api/v1/namespaces/%s/endpoints/", namespace); final String domain = getEnvOrDefault("DNS_DOMAIN", "cluster.local"); final String host = getEnvOrDefault("KUBERNETES_MASTER", "https://kubernetes.default.svc.".concat(domain)); log.info("Asking k8s registry at {}..", host); final List hazelcastEndpoints = new CopyOnWriteArrayList<>(); try { final String token = getServiceAccountToken(); final SSLContext ctx = SSLContext.getInstance("SSL"); ctx.init(null, trustAll, new SecureRandom()); final URL url = new URL(host + path + serviceName); final HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(); // TODO: remove this when and replace with CA cert loading, when the CA is propogated // to all nodes on all platforms. conn.setSSLSocketFactory(ctx.getSocketFactory()); conn.setHostnameVerifier(trustAllHosts); conn.addRequestProperty("Authorization", "Bearer " + token); final ObjectMapper mapper = new ObjectMapper(); final Endpoints endpoints = mapper.readValue(conn.getInputStream(), Endpoints.class); if (endpoints != null) { if (endpoints.subsets != null && !endpoints.subsets.isEmpty()) { endpoints.subsets.forEach(subset -> { if (subset.addresses != null && !subset.addresses.isEmpty()) { subset.addresses.forEach( addr -> hazelcastEndpoints.add(addr.ip)); } else if (subset.notReadyAddresses != null && !subset.notReadyAddresses.isEmpty()) { // in case of a full cluster restart // no address might be ready, in order to allow the cluster // to start initially, we will use the not ready addresses // as fallback subset.notReadyAddresses.forEach( addr -> hazelcastEndpoints.add(addr.ip)); } else { log.warn("Could not find any hazelcast nodes."); } }); } } } catch (IOException | NoSuchAlgorithmException | KeyManagementException ex) { log.warn("Request to Kubernetes API failed", ex); } log.info("Found {} pods running Hazelcast.", hazelcastEndpoints.size()); runHazelcast(hazelcastEndpoints); } private void runHazelcast(final List nodes) { // configure Hazelcast instance final Config cfg = new Config(); cfg.setInstanceName(UUID.randomUUID().toString()); // group configuration final String HC_GROUP_NAME = getEnvOrDefault("HC_GROUP_NAME", "someGroup"); final String HC_GROUP_PASSWORD = getEnvOrDefault("HC_GROUP_PASSWORD", null); final int HC_PORT = Integer.parseInt(getEnvOrDefault("HC_PORT", "5701")); final String HC_REST_ENABLED = getEnvOrDefault("HC_REST_ENABLED", "false"); if (HC_GROUP_PASSWORD != null) { cfg.setGroupConfig(new GroupConfig(HC_GROUP_NAME, HC_GROUP_PASSWORD)); } else { cfg.setGroupConfig(new GroupConfig(HC_GROUP_NAME)); } cfg.setProperty("hazelcast.rest.enabled", HC_REST_ENABLED); // network configuration initialization final NetworkConfig netCfg = new NetworkConfig(); netCfg.setPortAutoIncrement(false); netCfg.setPort(HC_PORT); // multicast final MulticastConfig mcCfg = new MulticastConfig(); mcCfg.setEnabled(false); // tcp final TcpIpConfig tcpCfg = new TcpIpConfig(); nodes.forEach(tcpCfg::addMember); tcpCfg.setEnabled(true); // network join configuration final JoinConfig joinCfg = new JoinConfig(); joinCfg.setMulticastConfig(mcCfg); joinCfg.setTcpIpConfig(tcpCfg); netCfg.setJoin(joinCfg); // ssl netCfg.setSSLConfig(new SSLConfig().setEnabled(false)); // set it all cfg.setNetworkConfig(netCfg); // run Hazelcast.newHazelcastInstance(cfg); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy