Repository: hazelcast/hazelcast-kubernetes (java)

Issue:
Hazelcast Kubernetes tries to access serviceaccount-token even if DNS Lookup is chosen method
https://github.com/hazelcast/hazelcast-kubernetes/blob/master/src/main/java/com/hazelcast/kubernetes/HazelcastKubernetesDiscoveryStrategy.java#L45

On this line the Kubernetes API Client is constructed, even if DNS_LOOKUP is chosen as discovery strategy.
This results in an attempt to access `/var/run/secrets/kubernetes.io/serviceaccount/token` which while fail in environments where token does not get auto-mounted for security reasons.



Pull request description:
Avoid reading serviceaccount-token in DNS Mode
Auto-mounting the serviceaccount-token is sometimes disabled to avoid giving applications
access to the kubernetes api when they don't need it. The current method of constructing
an instance of `KubernetesConfig` will try to read the information from the mounted serviceaccount
token even if a Service DNS is specified. This will fail in environments described earlier.

The information gathered from reading the serviceaccount-token fields is only relevant
for API mode. This commit will avoid reading the token if DNS Lookup is configured.

This fixes #284

Patch:
diff --git a/README.md b/README.md
index c3ae3ed..1865298 100644
--- a/README.md
+++ b/README.md
@@ -66,7 +66,6 @@ Using Kubernetes API requires granting certain permissions. To grant them for 'd
 Hazelcast Kubernetes Discovery requires creating a service to PODs where Hazelcast is running. In case of using Kubernetes API mode, the service can be of any type.
 
 ```yaml
-apiVersion: v1
 kind: Service
 metadata:
   name: MY-SERVICE-NAME
@@ -128,7 +127,6 @@ You can use one of `service-name`,`service-label`(`service-label-name`, `service
 Headless service is a service of type `ClusterIP` with the `clusterIP` property set to `None`.
 
 ```yaml
-apiVersion: v1
 kind: Service
 metadata:
   name: MY-SERVICE-NAME
@@ -333,10 +331,6 @@ Additionally:
 
 All these features are already included in [Hazelcast Helm Charts](#helm-chart).
 
-## CP Subsystem
-
-Hazelcast CP Subsystem can be used safely in Kubernetes only if CP Subsystem Persistence is enabled (Enterprise Feature). Otherwise, a CP Subsystem Group may not recover after scaling the cluster or performing the rolling upgrade operation.
-
 ## Plugin Usages
 
 Apart from embedding Hazelcast in your application as described above, there are multiple other scenarios of how to use the Hazelcast Kubernetes plugin.
diff --git a/pom.xml b/pom.xml
index 459c4d3..74d5355 100644
--- a/pom.xml
+++ b/pom.xml
@@ -35,13 +35,13 @@
     <junit.version>4.13.1</junit.version>
     <wiremock.version>2.27.2</wiremock.version>
     <hamcrest.version>2.2</hamcrest.version>
-    <mockito.version>3.7.7</mockito.version>
+    <mockito.version>3.6.28</mockito.version>
     <powermock.version>2.0.9</powermock.version>
     <findbugs.annotations.version>3.0.1u2</findbugs.annotations.version>
 
-    <hazelcast.version>4.1.1</hazelcast.version>
+    <hazelcast.version>4.1</hazelcast.version>
 
-    <maven.checkstyle.plugin.version>3.1.2</maven.checkstyle.plugin.version>
+    <maven.checkstyle.plugin.version>3.1.1</maven.checkstyle.plugin.version>
     <maven.findbugs.plugin.version>3.0.5</maven.findbugs.plugin.version>
     <maven.surefire.plugin.version>2.22.2</maven.surefire.plugin.version>
     <maven.sonar.plugin.version>3.3.0.603</maven.sonar.plugin.version>
diff --git a/src/main/java/com/hazelcast/kubernetes/KubernetesConfig.java b/src/main/java/com/hazelcast/kubernetes/KubernetesConfig.java
index 4f68674..8ca4f3f 100644
--- a/src/main/java/com/hazelcast/kubernetes/KubernetesConfig.java
+++ b/src/main/java/com/hazelcast/kubernetes/KubernetesConfig.java
@@ -112,7 +112,7 @@ final class KubernetesConfig {
             namespace = System.getenv("OPENSHIFT_BUILD_NAMESPACE");
         }
 
-        if (namespace == null) {
+        if (namespace == null && getMode() == DiscoveryMode.KUBERNETES_API) {
             namespace = readNamespace();
         }
 
@@ -121,7 +121,7 @@ final class KubernetesConfig {
 
     private String getApiToken(Map<String, Comparable> properties) {
         String apiToken = getOrNull(properties, KUBERNETES_SYSTEM_PREFIX, KUBERNETES_API_TOKEN);
-        if (apiToken == null) {
+        if (apiToken == null && getMode() == DiscoveryMode.KUBERNETES_API) {
             apiToken = readAccountToken();
         }
         return apiToken;
@@ -129,7 +129,7 @@ final class KubernetesConfig {
 
     private String caCertificate(Map<String, Comparable> properties) {
         String caCertificate = getOrNull(properties, KUBERNETES_SYSTEM_PREFIX, KUBERNETES_CA_CERTIFICATE);
-        if (caCertificate == null) {
+        if (caCertificate == null && getMode() == DiscoveryMode.KUBERNETES_API) {
             caCertificate = readCaCertificate();
         }
         return caCertificate;


Test patch:
diff --git a/src/test/java/com/hazelcast/kubernetes/KubernetesConfigTest.java b/src/test/java/com/hazelcast/kubernetes/KubernetesConfigTest.java
index 8edfaaf..81118ae 100644
--- a/src/test/java/com/hazelcast/kubernetes/KubernetesConfigTest.java
+++ b/src/test/java/com/hazelcast/kubernetes/KubernetesConfigTest.java
@@ -46,6 +46,7 @@ import static com.hazelcast.kubernetes.KubernetesProperties.SERVICE_LABEL_VALUE;
 import static com.hazelcast.kubernetes.KubernetesProperties.SERVICE_NAME;
 import static com.hazelcast.kubernetes.KubernetesProperties.SERVICE_PORT;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.powermock.api.mockito.PowerMockito.doReturn;
 
 @RunWith(PowerMockRunner.class)
@@ -77,6 +78,30 @@ public class KubernetesConfigTest {
         assertEquals(servicePort, config.getServicePort());
     }
 
+    @Test
+    public void dnsLookupModeWithoutServiceAccountToken() {
+        // given
+        String serviceDns = "hazelcast.default.svc.cluster.local";
+        int serviceDnsTimeout = 10;
+        int servicePort = 5703;
+
+        Map<String, Comparable> properties = new HashMap<>();
+        properties.put(SERVICE_DNS.key(), serviceDns);
+        properties.put(SERVICE_DNS_TIMEOUT.key(), serviceDnsTimeout);
+        properties.put(SERVICE_PORT.key(), servicePort);
+
+        // when
+        KubernetesConfig config = new KubernetesConfig(properties);
+
+        // then
+        assertEquals(DiscoveryMode.DNS_LOOKUP, config.getMode());
+        assertEquals(serviceDns, config.getServiceDns());
+        assertEquals(serviceDnsTimeout, config.getServiceDnsTimeout());
+        assertEquals(servicePort, config.getServicePort());
+        assertNull(config.getKubernetesApiToken());
+        assertNull(config.getKubernetesCaCertificate());
+    }
+
     @Test
     public void kubernetesApiModeDefault() throws Exception {
         // given