You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2020/06/29 13:47:59 UTC

[karaf-cellar] branch master updated: Proposal: implemented better support to configure Kubernetes API client (#76)

This is an automated email from the ASF dual-hosted git repository.

jbonofre pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/karaf-cellar.git


The following commit(s) were added to refs/heads/master by this push:
     new ad58b14  Proposal: implemented better support to configure Kubernetes API client (#76)
ad58b14 is described below

commit ad58b147a2db44bbf02b7832d27cf6d7402459da
Author: Roland Hauser <so...@gmail.com>
AuthorDate: Mon Jun 29 15:47:52 2020 +0200

    Proposal: implemented better support to configure Kubernetes API client (#76)
    
    [KARAF-6530] Improve kubernetes discovery support
---
 kubernetes/pom.xml                                 |  12 +
 .../apache/karaf/cellar/kubernetes/ConfigKey.java  |  62 +++++
 .../kubernetes/KubernetesDiscoveryService.java     | 271 +++++++++++++++++++--
 .../KubernetesDiscoveryServiceFactory.java         |  75 +++++-
 .../karaf/cellar/kubernetes/ConfigKeyTest.java     | 129 ++++++++++
 .../KubernetesDiscoveryServiceFactoryTest.java     | 175 +++++++++++++
 .../kubernetes/KubernetesDiscoveryServiceTest.java | 167 +++++++++++++
 7 files changed, 861 insertions(+), 30 deletions(-)

diff --git a/kubernetes/pom.xml b/kubernetes/pom.xml
index 1892ce1..0b3b894 100644
--- a/kubernetes/pom.xml
+++ b/kubernetes/pom.xml
@@ -47,6 +47,18 @@
             <artifactId>kubernetes-client</artifactId>
         </dependency>
 
+        <!-- Testing -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.easymock</groupId>
+            <artifactId>easymock</artifactId>
+            <scope>test</scope>
+        </dependency>
+
         <!-- OSGi -->
         <dependency>
             <groupId>org.apache.karaf</groupId>
diff --git a/kubernetes/src/main/java/org/apache/karaf/cellar/kubernetes/ConfigKey.java b/kubernetes/src/main/java/org/apache/karaf/cellar/kubernetes/ConfigKey.java
new file mode 100644
index 0000000..c26c5b8
--- /dev/null
+++ b/kubernetes/src/main/java/org/apache/karaf/cellar/kubernetes/ConfigKey.java
@@ -0,0 +1,62 @@
+package org.apache.karaf.cellar.kubernetes;
+
+import io.fabric8.kubernetes.client.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Dictionary;
+
+/**
+ * Constant names must be the same as the envvar names specified on
+ * <a href="https://github.com/fabric8io/kubernetes-client">GitHub</a>
+ */
+public enum ConfigKey {
+    KUBERNETES_MASTER(Config.KUBERNETES_MASTER_SYSTEM_PROPERTY),
+    KUBERNETES_API_VERSION(Config.KUBERNETES_API_VERSION_SYSTEM_PROPERTY),
+    KUBERNETES_TRUST_CERTIFICATES(Config.KUBERNETES_TRUST_CERT_SYSTEM_PROPERTY),
+    KUBERNETES_DISABLE_HOSTNAME_VERIFICATION(Config.KUBERNETES_DISABLE_HOSTNAME_VERIFICATION_SYSTEM_PROPERTY),
+    KUBERNETES_CERTS_CA_FILE(Config.KUBERNETES_CA_CERTIFICATE_FILE_SYSTEM_PROPERTY),
+    KUBERNETES_CERTS_CA_DATA(Config.KUBERNETES_CA_CERTIFICATE_DATA_SYSTEM_PROPERTY),
+    KUBERNETES_CERTS_CLIENT_FILE(Config.KUBERNETES_CLIENT_CERTIFICATE_FILE_SYSTEM_PROPERTY),
+    KUBERNETES_CERTS_CLIENT_DATA(Config.KUBERNETES_CLIENT_CERTIFICATE_DATA_SYSTEM_PROPERTY),
+    KUBERNETES_CERTS_CLIENT_KEY_FILE(Config.KUBERNETES_CLIENT_KEY_FILE_SYSTEM_PROPERTY),
+    KUBERNETES_CERTS_CLIENT_KEY_DATA(Config.KUBERNETES_CLIENT_KEY_DATA_SYSTEM_PROPERTY),
+    KUBERNETES_CERTS_CLIENT_KEY_ALGO(Config.KUBERNETES_CLIENT_KEY_ALGO_SYSTEM_PROPERTY),
+    KUBERNETES_CERTS_CLIENT_KEY_PASSPHRASE(Config.KUBERNETES_CLIENT_KEY_PASSPHRASE_SYSTEM_PROPERTY),
+    KUBERNETES_AUTH_BASIC_USERNAME(Config.KUBERNETES_AUTH_BASIC_USERNAME_SYSTEM_PROPERTY),
+    KUBERNETES_AUTH_BASIC_PASSWORD(Config.KUBERNETES_AUTH_BASIC_PASSWORD_SYSTEM_PROPERTY),
+    KUBERNETES_AUTH_TOKEN(Config.KUBERNETES_OAUTH_TOKEN_SYSTEM_PROPERTY),
+    KUBERNETES_WATCH_RECONNECTINTERVAL(Config.KUBERNETES_WATCH_RECONNECT_INTERVAL_SYSTEM_PROPERTY),
+    KUBERNETES_WATCH_RECONNECTLIMIT(Config.KUBERNETES_WATCH_RECONNECT_LIMIT_SYSTEM_PROPERTY),
+    KUBERNETES_USER_AGENT(Config.KUBERNETES_USER_AGENT),
+    KUBERNETES_TLS_VERSIONS(Config.KUBERNETES_TLS_VERSIONS),
+    KUBERNETES_TRUSTSTORE_FILE(Config.KUBERNETES_TRUSTSTORE_FILE_PROPERTY),
+    KUBERNETES_TRUSTSTORE_PASSPHRASE(Config.KUBERNETES_TRUSTSTORE_PASSPHRASE_PROPERTY),
+    KUBERNETES_KEYSTORE_FILE(Config.KUBERNETES_KEYSTORE_FILE_PROPERTY),
+    KUBERNETES_KEYSTORE_PASSPHRASE(Config.KUBERNETES_KEYSTORE_PASSPHRASE_PROPERTY);
+    private static final Logger LOGGER = LoggerFactory.getLogger(ConfigKey.class);
+    String propertyName;
+
+    ConfigKey(String propertyName) {
+        this.propertyName = propertyName;
+    }
+
+    public String getValue(Dictionary properties) {
+        // Highest priority
+        String value = (String)properties.get(propertyName);
+        LOGGER.debug("Properties : {}", value);
+
+        if (value == null) {
+            // Second priority
+            value = System.getProperty(propertyName);
+            LOGGER.debug("System properties : {}", value);
+
+            if (value == null) {
+                value = System.getenv(name());
+                LOGGER.debug("Environment variables : {}", value);
+            }
+        }
+
+        return value;
+    }
+}
diff --git a/kubernetes/src/main/java/org/apache/karaf/cellar/kubernetes/KubernetesDiscoveryService.java b/kubernetes/src/main/java/org/apache/karaf/cellar/kubernetes/KubernetesDiscoveryService.java
index e4f0890..bed2dcb 100644
--- a/kubernetes/src/main/java/org/apache/karaf/cellar/kubernetes/KubernetesDiscoveryService.java
+++ b/kubernetes/src/main/java/org/apache/karaf/cellar/kubernetes/KubernetesDiscoveryService.java
@@ -13,13 +13,13 @@
  */
 package org.apache.karaf.cellar.kubernetes;
 
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.PodList;
 import io.fabric8.kubernetes.client.Config;
 import io.fabric8.kubernetes.client.ConfigBuilder;
 import io.fabric8.kubernetes.client.DefaultKubernetesClient;
 import io.fabric8.kubernetes.client.KubernetesClient;
-import io.fabric8.kubernetes.api.model.Pod;
-import io.fabric8.kubernetes.api.model.PodList;
-
+import okhttp3.TlsVersion;
 import org.apache.karaf.cellar.core.discovery.DiscoveryService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -35,8 +35,29 @@ public class KubernetesDiscoveryService implements DiscoveryService {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(KubernetesDiscoveryService.class);
 
-    private String kubernetesHost;
-    private String kubernetesPort;
+    private String kubernetesMaster;
+    private String kubernetesApiVersion;
+    private boolean kubernetesTrustCertificates;
+    private boolean kubernetesDisableHostnameVerification;
+    private String kubernetesCertsCaFile;
+    private String kubernetesCertsCaData;
+    private String kubernetesCertsClientFile;
+    private String kubernetesCertsClientData;
+    private String kubernetesCertsClientKeyFile;
+    private String kubernetesCertsClientKeyData;
+    private String kubernetesCertsClientKeyAlgo;
+    private String kubernetesCertsClientKeyPassphrase;
+    private String kubernetesAuthBasicUsername;
+    private String kubernetesAuthBasicPassword;
+    private String kubernetesOauthToken;
+    private int kubernetesWatchReconnectInterval;
+    private int kubernetesWatchReconnectLimit;
+    private String kubernetesUserAgent;
+    private String kubernetesTlsVersion;
+    private String kubernetesTruststoreFile;
+    private String kubernetesTruststorePassphrase;
+    private String kubernetesKeystoreFile;
+    private String kubernetesKeystorePassphrase;
     private String kubernetesPodLabelKey;
     private String kubernetesPodLabelValue;
 
@@ -46,11 +67,38 @@ public class KubernetesDiscoveryService implements DiscoveryService {
         LOGGER.debug("CELLAR KUBERNETES: create discovery service");
     }
 
+    Config createConfig() {
+        return new ConfigBuilder()
+                .withMasterUrl(kubernetesMaster)
+                .withApiVersion(kubernetesApiVersion)
+                .withTrustCerts(kubernetesTrustCertificates)
+                .withDisableHostnameVerification(kubernetesDisableHostnameVerification)
+                .withCaCertFile(kubernetesCertsCaFile)
+                .withCaCertData(kubernetesCertsCaData)
+                .withClientCertFile(kubernetesCertsClientFile)
+                .withClientCertData(kubernetesCertsClientData)
+                .withClientKeyFile(kubernetesCertsClientKeyFile)
+                .withClientKeyData(kubernetesCertsClientKeyData)
+                .withClientKeyAlgo(kubernetesCertsClientKeyAlgo)
+                .withClientKeyPassphrase(kubernetesCertsClientKeyPassphrase)
+                .withUsername(kubernetesAuthBasicUsername)
+                .withPassword(kubernetesAuthBasicPassword)
+                .withOauthToken(kubernetesOauthToken)
+                .withWatchReconnectInterval(kubernetesWatchReconnectInterval)
+                .withWatchReconnectLimit(kubernetesWatchReconnectLimit)
+                .withUserAgent(kubernetesUserAgent)
+                .withTlsVersions(TlsVersion.forJavaName(kubernetesTlsVersion))
+                .withTrustStoreFile(kubernetesTruststoreFile)
+                .withTrustStorePassphrase(kubernetesTruststorePassphrase)
+                .withKeyStoreFile(kubernetesKeystoreFile)
+                .withKeyStorePassphrase(kubernetesKeystorePassphrase)
+                .build();
+    }
+
     public void init() {
         try {
-            String kubernetesUrl = "http://" + kubernetesHost + ":" + kubernetesPort;
-            LOGGER.debug("CELLAR KUBERNETES: query API at {} ...", kubernetesUrl);
-            Config config = new ConfigBuilder().withMasterUrl(kubernetesUrl).build();
+            LOGGER.debug("CELLAR KUBERNETES: query API at {} ...", kubernetesMaster);
+            Config config = createConfig();
             kubernetesClient = new DefaultKubernetesClient(config);
             LOGGER.debug("CELLAR KUBERNETES: discovery service initialized");
         } catch (Exception e) {
@@ -99,20 +147,8 @@ public class KubernetesDiscoveryService implements DiscoveryService {
         // nothing to do for Kubernetes
     }
 
-    public String getKubernetesHost() {
-        return kubernetesHost;
-    }
-
-    public void setKubernetesHost(String kubernetesHost) {
-        this.kubernetesHost = kubernetesHost;
-    }
-
-    public String getKubernetesPort() {
-        return kubernetesPort;
-    }
-
-    public void setKubernetesPort(String kubernetesPort) {
-        this.kubernetesPort = kubernetesPort;
+    void setKubernetesClient(KubernetesClient kubernetesClient) {
+        this.kubernetesClient = kubernetesClient;
     }
 
     public String getKubernetesPodLabelKey() {
@@ -131,4 +167,195 @@ public class KubernetesDiscoveryService implements DiscoveryService {
         this.kubernetesPodLabelValue = kubernetesPodLabelValue;
     }
 
+    public String getKubernetesMaster() {
+        return kubernetesMaster;
+    }
+
+    public void setKubernetesMaster(String kubernetesMaster) {
+        this.kubernetesMaster = kubernetesMaster;
+    }
+
+    public String getKubernetesApiVersion() {
+        return kubernetesApiVersion;
+    }
+
+    public void setKubernetesApiVersion(String kubernetesApiVersion) {
+        this.kubernetesApiVersion = kubernetesApiVersion;
+    }
+
+    public boolean isKubernetesTrustCertificates() {
+        return kubernetesTrustCertificates;
+    }
+
+    public void setKubernetesTrustCertificates(String kubernetesTrustCertificates) {
+        if (kubernetesTrustCertificates != null) {
+            this.kubernetesTrustCertificates = Boolean.parseBoolean(kubernetesTrustCertificates);
+        }
+    }
+
+    public boolean isKubernetesDisableHostnameVerification() {
+        return kubernetesDisableHostnameVerification;
+    }
+
+    public void setKubernetesDisableHostnameVerification(String kubernetesDisableHostnameVerification) {
+        if (kubernetesDisableHostnameVerification != null) {
+            this.kubernetesDisableHostnameVerification = Boolean.parseBoolean(kubernetesDisableHostnameVerification);
+        }
+    }
+
+    public String getKubernetesCertsCaFile() {
+        return kubernetesCertsCaFile;
+    }
+
+    public void setKubernetesCertsCaFile(String kubernetesCertsCaFile) {
+        this.kubernetesCertsCaFile = kubernetesCertsCaFile;
+    }
+
+    public String getKubernetesCertsCaData() {
+        return kubernetesCertsCaData;
+    }
+
+    public void setKubernetesCertsCaData(String kubernetesCertsCaData) {
+        this.kubernetesCertsCaData = kubernetesCertsCaData;
+    }
+
+    public String getKubernetesCertsClientFile() {
+        return kubernetesCertsClientFile;
+    }
+
+    public void setKubernetesCertsClientFile(String kubernetesCertsClientFile) {
+        this.kubernetesCertsClientFile = kubernetesCertsClientFile;
+    }
+
+    public String getKubernetesCertsClientData() {
+        return kubernetesCertsClientData;
+    }
+
+    public void setKubernetesCertsClientData(String kubernetesCertsClientData) {
+        this.kubernetesCertsClientData = kubernetesCertsClientData;
+    }
+
+    public String getKubernetesCertsClientKeyFile() {
+        return kubernetesCertsClientKeyFile;
+    }
+
+    public void setKubernetesCertsClientKeyFile(String kubernetesCertsClientKeyFile) {
+        this.kubernetesCertsClientKeyFile = kubernetesCertsClientKeyFile;
+    }
+
+    public String getKubernetesCertsClientKeyData() {
+        return kubernetesCertsClientKeyData;
+    }
+
+    public void setKubernetesCertsClientKeyData(String kubernetesCertsClientKeyData) {
+        this.kubernetesCertsClientKeyData = kubernetesCertsClientKeyData;
+    }
+
+    public String getKubernetesCertsClientKeyAlgo() {
+        return kubernetesCertsClientKeyAlgo;
+    }
+
+    public void setKubernetesCertsClientKeyAlgo(String kubernetesCertsClientKeyAlgo) {
+        this.kubernetesCertsClientKeyAlgo = kubernetesCertsClientKeyAlgo;
+    }
+
+    public String getKubernetesCertsClientKeyPassphrase() {
+        return kubernetesCertsClientKeyPassphrase;
+    }
+
+    public void setKubernetesCertsClientKeyPassphrase(String kubernetesCertsClientKeyPassphrase) {
+        this.kubernetesCertsClientKeyPassphrase = kubernetesCertsClientKeyPassphrase;
+    }
+
+    public String getKubernetesAuthBasicUsername() {
+        return kubernetesAuthBasicUsername;
+    }
+
+    public void setKubernetesAuthBasicUsername(String kubernetesAuthBasicUsername) {
+        this.kubernetesAuthBasicUsername = kubernetesAuthBasicUsername;
+    }
+
+    public String getKubernetesAuthBasicPassword() {
+        return kubernetesAuthBasicPassword;
+    }
+
+    public void setKubernetesAuthBasicPassword(String kubernetesAuthBasicPassword) {
+        this.kubernetesAuthBasicPassword = kubernetesAuthBasicPassword;
+    }
+
+    public String getKubernetesOauthToken() {
+        return kubernetesOauthToken;
+    }
+
+    public void setKubernetesOauthToken(String kubernetesOauthToken) {
+        this.kubernetesOauthToken = kubernetesOauthToken;
+    }
+
+    public int getKubernetesWatchReconnectInterval() {
+        return kubernetesWatchReconnectInterval;
+    }
+
+    public void setKubernetesWatchReconnectInterval(String kubernetesWatchReconnectInterval) {
+        if (kubernetesWatchReconnectInterval != null) {
+            this.kubernetesWatchReconnectInterval = Integer.parseInt(kubernetesWatchReconnectInterval);
+        }
+    }
+
+    public int getKubernetesWatchReconnectLimit() {
+        return kubernetesWatchReconnectLimit;
+    }
+
+    public void setKubernetesWatchReconnectLimit(String kubernetesWatchReconnectLimit) {
+        if (kubernetesWatchReconnectLimit != null) {
+            this.kubernetesWatchReconnectLimit = Integer.parseInt(kubernetesWatchReconnectLimit);
+        }
+    }
+
+    public String getKubernetesUserAgent() {
+        return kubernetesUserAgent;
+    }
+
+    public void setKubernetesUserAgent(String kubernetesUserAgent) {
+        this.kubernetesUserAgent = kubernetesUserAgent;
+    }
+
+    public String getKubernetesTlsVersion() {
+        return kubernetesTlsVersion;
+    }
+
+    public void setKubernetesTlsVersion(String kubernetesTlsVersion) {
+        this.kubernetesTlsVersion = kubernetesTlsVersion;
+    }
+
+    public String getKubernetesTruststoreFile() {
+        return kubernetesTruststoreFile;
+    }
+
+    public void setKubernetesTruststoreFile(String kubernetesTruststoreFile) {
+        this.kubernetesTruststoreFile = kubernetesTruststoreFile;
+    }
+
+    public String getKubernetesTruststorePassphrase() {
+        return kubernetesTruststorePassphrase;
+    }
+
+    public void setKubernetesTruststorePassphrase(String kubernetesTruststorePassphrase) {
+        this.kubernetesTruststorePassphrase = kubernetesTruststorePassphrase;
+    }
+
+    public String getKubernetesKeystoreFile() {
+        return kubernetesKeystoreFile;
+    }
+
+    public void setKubernetesKeystoreFile(String kubernetesKeystoreFile) {
+        this.kubernetesKeystoreFile = kubernetesKeystoreFile;
+    }
+
+    public String getKubernetesKeystorePassphrase() {
+        return kubernetesKeystorePassphrase;
+    }
+
+    public void setKubernetesKeystorePassphrase(String kubernetesKeystorePassphrase) {
+        this.kubernetesKeystorePassphrase = kubernetesKeystorePassphrase;
+    }
 }
diff --git a/kubernetes/src/main/java/org/apache/karaf/cellar/kubernetes/KubernetesDiscoveryServiceFactory.java b/kubernetes/src/main/java/org/apache/karaf/cellar/kubernetes/KubernetesDiscoveryServiceFactory.java
index 0eb7aae..44b73dc 100644
--- a/kubernetes/src/main/java/org/apache/karaf/cellar/kubernetes/KubernetesDiscoveryServiceFactory.java
+++ b/kubernetes/src/main/java/org/apache/karaf/cellar/kubernetes/KubernetesDiscoveryServiceFactory.java
@@ -27,6 +27,30 @@ import java.util.Map;
 import java.util.Properties;
 import java.util.concurrent.ConcurrentHashMap;
 
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_API_VERSION;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_AUTH_BASIC_PASSWORD;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_AUTH_BASIC_USERNAME;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_AUTH_TOKEN;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CA_DATA;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CA_FILE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CLIENT_DATA;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CLIENT_FILE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CLIENT_KEY_ALGO;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CLIENT_KEY_DATA;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CLIENT_KEY_FILE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CLIENT_KEY_PASSPHRASE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_DISABLE_HOSTNAME_VERIFICATION;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_KEYSTORE_FILE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_KEYSTORE_PASSPHRASE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_MASTER;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_TLS_VERSIONS;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_TRUSTSTORE_FILE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_TRUSTSTORE_PASSPHRASE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_TRUST_CERTIFICATES;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_USER_AGENT;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_WATCH_RECONNECTINTERVAL;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_WATCH_RECONNECTLIMIT;
+
 /**
  * A factory for Kubernetes discovery services.
  */
@@ -39,10 +63,16 @@ public class KubernetesDiscoveryServiceFactory implements ManagedServiceFactory
 
     private static final Logger LOGGER = LoggerFactory.getLogger(KubernetesDiscoveryServiceFactory.class);
 
-    private static final String KUBERNETES_HOST = "host";
-    private static final String KUBERNETES_PORT = "port";
-    private static final String KUBERNETES_POD_LABEL_KEY = "pod.label.key";
-    private static final String KUBERNETES_POD_LABEL_VALUE = "pod.label.value";
+    // Deprecated constants, use the fabric8 default ones -> see io.fabric8.kubernetes.client.Config
+    @Deprecated
+    static final String KUBERNETES_HOST = "host";
+    @Deprecated
+    static final String KUBERNETES_PORT = "port";
+
+    static final String KUBERNETES_POD_LABEL_KEY = "pod.label.key";
+    static final String KUBERNETES_POD_LABEL_VALUE = "pod.label.value";
+    static final String DEFAULT_POD_LABEL_KEY = "name";
+    static final String DEFAULT_POD_LABEL_VALUE = "cellar";
 
     private final Map<String, ServiceRegistration> registrations = new ConcurrentHashMap<String, ServiceRegistration>();
 
@@ -86,15 +116,44 @@ public class KubernetesDiscoveryServiceFactory implements ManagedServiceFactory
                 }
                 String kubernetesPodLabelKey = (String) properties.get(KUBERNETES_POD_LABEL_KEY);
                 if (kubernetesPodLabelKey == null) {
-                    kubernetesPodLabelKey = "name";
+                    kubernetesPodLabelKey = DEFAULT_POD_LABEL_KEY;
                 }
                 String kubernetesPodLabelValue = (String) properties.get(KUBERNETES_POD_LABEL_VALUE);
                 if (kubernetesPodLabelValue == null) {
-                    kubernetesPodLabelValue = "cellar";
+                    kubernetesPodLabelValue = DEFAULT_POD_LABEL_VALUE;
+                }
+
+
+                String kubernetesMaster = KUBERNETES_MASTER.getValue(properties);
+
+                // Keep compatibility with old configuration scheme
+                if (kubernetesMaster == null) {
+                    kubernetesMaster = "http://" + kubernetesHost + ":" + kubernetesPort;
                 }
 
-                kubernetesDiscoveryService.setKubernetesHost(kubernetesHost);
-                kubernetesDiscoveryService.setKubernetesPort(kubernetesPort);
+                kubernetesDiscoveryService.setKubernetesMaster(kubernetesMaster);
+                kubernetesDiscoveryService.setKubernetesApiVersion(KUBERNETES_API_VERSION.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesTrustCertificates(KUBERNETES_TRUST_CERTIFICATES.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesDisableHostnameVerification(KUBERNETES_DISABLE_HOSTNAME_VERIFICATION.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesCertsCaFile(KUBERNETES_CERTS_CA_FILE.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesCertsCaData(KUBERNETES_CERTS_CA_DATA.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesCertsClientFile(KUBERNETES_CERTS_CLIENT_FILE.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesCertsClientData(KUBERNETES_CERTS_CLIENT_DATA.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesCertsClientKeyFile(KUBERNETES_CERTS_CLIENT_KEY_FILE.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesCertsClientKeyData(KUBERNETES_CERTS_CLIENT_KEY_DATA.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesCertsClientKeyAlgo(KUBERNETES_CERTS_CLIENT_KEY_ALGO.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesCertsClientKeyPassphrase(KUBERNETES_CERTS_CLIENT_KEY_PASSPHRASE.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesAuthBasicUsername(KUBERNETES_AUTH_BASIC_USERNAME.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesAuthBasicPassword(KUBERNETES_AUTH_BASIC_PASSWORD.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesOauthToken(KUBERNETES_AUTH_TOKEN.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesWatchReconnectInterval(KUBERNETES_WATCH_RECONNECTINTERVAL.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesWatchReconnectLimit(KUBERNETES_WATCH_RECONNECTLIMIT.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesUserAgent(KUBERNETES_USER_AGENT.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesTlsVersion(KUBERNETES_TLS_VERSIONS.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesTruststoreFile(KUBERNETES_TRUSTSTORE_FILE.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesTruststorePassphrase(KUBERNETES_TRUSTSTORE_PASSPHRASE.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesKeystoreFile(KUBERNETES_KEYSTORE_FILE.getValue(properties));
+                kubernetesDiscoveryService.setKubernetesKeystorePassphrase(KUBERNETES_KEYSTORE_PASSPHRASE.getValue(properties));
                 kubernetesDiscoveryService.setKubernetesPodLabelKey(kubernetesPodLabelKey);
                 kubernetesDiscoveryService.setKubernetesPodLabelValue(kubernetesPodLabelValue);
 
diff --git a/kubernetes/src/test/java/org/apache/karaf/cellar/kubernetes/ConfigKeyTest.java b/kubernetes/src/test/java/org/apache/karaf/cellar/kubernetes/ConfigKeyTest.java
new file mode 100644
index 0000000..48a9f68
--- /dev/null
+++ b/kubernetes/src/test/java/org/apache/karaf/cellar/kubernetes/ConfigKeyTest.java
@@ -0,0 +1,129 @@
+package org.apache.karaf.cellar.kubernetes;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class ConfigKeyTest {
+    private static final String EXPECTED_PROP_VALUE = "properties value";
+    private static final String EXPECTED_SYSPROP_VALUE = "system properties value";
+    private static final String EXPECTED_ENVVAR_VALUE = "envvar value";
+    private Properties originalProperties = new Properties();
+
+    public static class GetValueTestRun {
+
+        public static void main(String[] args) {
+            if (args.length == 0) {
+                System.exit(1);
+            }
+
+            String constantName = args[0];
+            String expectedValue = args[1];
+            ConfigKey key = ConfigKey.valueOf(constantName);
+
+            if (!expectedValue.equals(key.getValue(new Hashtable<>()))) {
+                System.exit(2);
+            }
+        }
+
+    }
+
+    @Before
+    public void setup() {
+        originalProperties.putAll(System.getProperties());
+    }
+
+    @After
+    public void tearDown() {
+        System.setProperties(originalProperties);
+    }
+
+    private void verifyEnvvar(ConfigKey key) throws Exception {
+        startTestProc(key, EXPECTED_ENVVAR_VALUE, null);
+    }
+
+    private void verifySysProp(ConfigKey key) throws Exception {
+        startTestProc(key, EXPECTED_ENVVAR_VALUE, EXPECTED_SYSPROP_VALUE);
+    }
+
+    private void startTestProc(ConfigKey key, String expectedEnvvarValue, String expectedSyspropValue) throws Exception {
+        String expectedValue = expectedSyspropValue == null ? expectedEnvvarValue : expectedSyspropValue;
+
+        String javaHome = System.getProperty("java.home");
+        String extension = System.getProperty("os.name").toLowerCase().contains("windows") ? ".exe" : "";
+        String javaBin = String.format("%s%sbin%sjava%s", javaHome, File.separator, File.separator, extension);
+        String classpath = System.getProperty("java.class.path");
+        List<String> command = new LinkedList<>();
+        command.add(javaBin);
+
+        if (expectedSyspropValue != null) {
+            command.add(String.format("-D%s=%s", key.propertyName, expectedSyspropValue));
+        }
+
+        command.add("-cp");
+        command.add(classpath);
+        command.add(GetValueTestRun.class.getName());
+        command.add(key.name());
+        command.add(expectedValue);
+
+        ProcessBuilder builder = new ProcessBuilder(command);
+        builder.inheritIO();
+        Map<String, String> env = builder.environment();
+        env.put(key.name(), expectedEnvvarValue);
+        Process proc = builder.start();
+        int rc = proc.waitFor();
+
+        if (rc == 1) {
+            Assert.fail("An unexpected exception occurred!");
+        }
+        if (rc == 2) {
+            Assert.fail(String.format("Actual value differs from %s", expectedValue));
+        }
+    }
+
+    @Test
+    public void valueFromEnvvar() throws Exception {
+        for (ConfigKey key : ConfigKey.values()) {
+            verifyEnvvar(key);
+        }
+    }
+
+    @Test
+    public void valueFromSystemProps() throws Exception {
+        for (ConfigKey key : ConfigKey.values()) {
+            verifySysProp(key);
+        }
+    }
+
+    @Test
+    public void valueFromProperties() {
+        for (ConfigKey key : ConfigKey.values()) {
+            System.setProperty(key.propertyName, EXPECTED_SYSPROP_VALUE);
+            Dictionary<String, String> properties = new Hashtable<>();
+            properties.put(key.propertyName, EXPECTED_PROP_VALUE);
+            assertEquals(EXPECTED_PROP_VALUE, key.getValue(properties));
+        }
+    }
+
+    @Test
+    public void verifyConfigKeyUniquelyAssignedToEnvvar() {
+        Set<String> configKeys = new HashSet<>();
+        for (ConfigKey key : ConfigKey.values()) {
+            assertTrue(configKeys.add(key.propertyName));
+        }
+    }
+}
diff --git a/kubernetes/src/test/java/org/apache/karaf/cellar/kubernetes/KubernetesDiscoveryServiceFactoryTest.java b/kubernetes/src/test/java/org/apache/karaf/cellar/kubernetes/KubernetesDiscoveryServiceFactoryTest.java
new file mode 100644
index 0000000..01899c3
--- /dev/null
+++ b/kubernetes/src/test/java/org/apache/karaf/cellar/kubernetes/KubernetesDiscoveryServiceFactoryTest.java
@@ -0,0 +1,175 @@
+package org.apache.karaf.cellar.kubernetes;
+
+import org.apache.karaf.cellar.core.discovery.DiscoveryService;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_API_VERSION;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_AUTH_BASIC_PASSWORD;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_AUTH_BASIC_USERNAME;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_AUTH_TOKEN;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CA_DATA;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CA_FILE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CLIENT_DATA;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CLIENT_FILE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CLIENT_KEY_ALGO;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CLIENT_KEY_DATA;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CLIENT_KEY_FILE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_CERTS_CLIENT_KEY_PASSPHRASE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_DISABLE_HOSTNAME_VERIFICATION;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_KEYSTORE_FILE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_KEYSTORE_PASSPHRASE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_TLS_VERSIONS;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_TRUSTSTORE_FILE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_TRUSTSTORE_PASSPHRASE;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_TRUST_CERTIFICATES;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_USER_AGENT;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_WATCH_RECONNECTINTERVAL;
+import static org.apache.karaf.cellar.kubernetes.ConfigKey.KUBERNETES_WATCH_RECONNECTLIMIT;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_API_VERSION;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_AUTH_BASIC_PASSWORD;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_AUTH_BASIC_USERNAME;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_CERTS_CA_DATA;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_CERTS_CA_FILE;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_CERTS_CLIENT_DATA;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_CERTS_CLIENT_FILE;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_ALGO;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_DATA;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_FILE;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_PASSPHRASE;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_DISABLE_HOSTNAME_VERIFICATION;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_KEYSTORE_FILE;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_KEYSTORE_PASSPHRASE;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_MASTER;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_OAUTH_TOKEN;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_TLS_VERSION;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_TRUSTSTORE_FILE;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_TRUSTSTORE_PASSPHRASE;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_TRUST_CERTIFICATES;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_USER_AGENT;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_WATCH_RECONNECT_INTERVAL;
+import static org.apache.karaf.cellar.kubernetes.KubernetesDiscoveryServiceTest.EXPECTED_KUBERNETES_WATCH_RECONNECT_LIMIT;
+import static org.easymock.EasyMock.capture;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.newCapture;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertEquals;
+
+public class KubernetesDiscoveryServiceFactoryTest {
+    private static final String ANY_PID = "anyPid";
+    private static final String EXPECTED_POD_LABEL_KEY = "expectedPodLabelKey";
+    private static final String EXPECTED_POD_LABEL_VALUE = "expectedPodLabelValue";
+    private final ServiceRegistration registration = EasyMock.mock(ServiceRegistration.class);
+    private final BundleContext bundleContext = EasyMock.mock(BundleContext.class);
+    private final KubernetesDiscoveryServiceFactory serviceFactory = new KubernetesDiscoveryServiceFactory(bundleContext);
+    private final Dictionary<String, String> properties = new Hashtable<>();
+    private final Capture<Object> serviceCapture = newCapture();
+    private final Capture<Dictionary<String, ?>> servicePropertiesCapture = newCapture();
+    private Dictionary<String, ?> serviceProperties;
+    private KubernetesDiscoveryService registeredService;
+
+    @Before
+    public void setup() throws Exception {
+        expect(bundleContext.registerService(
+                eq(DiscoveryService.class.getName()),
+                capture(serviceCapture),
+                capture(servicePropertiesCapture))).andReturn(registration);
+        replay(bundleContext);
+    }
+
+    private void update() throws Exception {
+        serviceFactory.updated(ANY_PID, properties);
+        this.registeredService = (KubernetesDiscoveryService) serviceCapture.getValue();
+        this.serviceProperties = servicePropertiesCapture.getValue();
+    }
+
+    @Test
+    public void verifyUpdatedOldConfigScheme() throws Exception {
+        properties.put(KubernetesDiscoveryServiceFactory.KUBERNETES_HOST, "foo");
+        properties.put(KubernetesDiscoveryServiceFactory.KUBERNETES_PORT, "55555");
+        update();
+        assertEquals("http://foo:55555", registeredService.getKubernetesMaster());
+    }
+
+    @Test
+    public void verifyUpdatedNewMasterHasPrecedence() throws Exception {
+        properties.put(KubernetesDiscoveryServiceFactory.KUBERNETES_HOST, "foo");
+        properties.put(KubernetesDiscoveryServiceFactory.KUBERNETES_PORT, "55555");
+        properties.put(ConfigKey.KUBERNETES_MASTER.propertyName, EXPECTED_KUBERNETES_MASTER);
+        update();
+        assertEquals(EXPECTED_KUBERNETES_MASTER, registeredService.getKubernetesMaster());
+    }
+
+    @Test
+    public void verifyUpdatedDefaultPodKeyAndLabel() throws Exception {
+        update();
+        assertEquals(KubernetesDiscoveryServiceFactory.DEFAULT_POD_LABEL_KEY, registeredService.getKubernetesPodLabelKey());
+        assertEquals(KubernetesDiscoveryServiceFactory.DEFAULT_POD_LABEL_VALUE, registeredService.getKubernetesPodLabelValue());
+    }
+
+    @Test
+    public void verifyUpdatedPodKeyAndLabel() throws Exception {
+        properties.put(KubernetesDiscoveryServiceFactory.KUBERNETES_POD_LABEL_KEY, EXPECTED_POD_LABEL_KEY);
+        properties.put(KubernetesDiscoveryServiceFactory.KUBERNETES_POD_LABEL_VALUE, EXPECTED_POD_LABEL_VALUE);
+        update();
+        assertEquals(EXPECTED_POD_LABEL_KEY, registeredService.getKubernetesPodLabelKey());
+        assertEquals(EXPECTED_POD_LABEL_VALUE, registeredService.getKubernetesPodLabelValue());
+    }
+
+    @Test
+    public void verifyUpdateKubernetesConfig() throws Exception {
+        properties.put(KUBERNETES_API_VERSION.propertyName, EXPECTED_KUBERNETES_API_VERSION);
+        properties.put(KUBERNETES_TRUST_CERTIFICATES.propertyName, EXPECTED_KUBERNETES_TRUST_CERTIFICATES);
+        properties.put(KUBERNETES_DISABLE_HOSTNAME_VERIFICATION.propertyName, EXPECTED_KUBERNETES_DISABLE_HOSTNAME_VERIFICATION);
+        properties.put(KUBERNETES_CERTS_CA_FILE.propertyName, EXPECTED_KUBERNETES_CERTS_CA_FILE);
+        properties.put(KUBERNETES_CERTS_CA_DATA.propertyName, EXPECTED_KUBERNETES_CERTS_CA_DATA);
+        properties.put(KUBERNETES_CERTS_CLIENT_FILE.propertyName, EXPECTED_KUBERNETES_CERTS_CLIENT_FILE);
+        properties.put(KUBERNETES_CERTS_CLIENT_DATA.propertyName, EXPECTED_KUBERNETES_CERTS_CLIENT_DATA);
+        properties.put(KUBERNETES_CERTS_CLIENT_KEY_FILE.propertyName, EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_FILE);
+        properties.put(KUBERNETES_CERTS_CLIENT_KEY_DATA.propertyName, EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_DATA);
+        properties.put(KUBERNETES_CERTS_CLIENT_KEY_ALGO.propertyName, EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_ALGO);
+        properties.put(KUBERNETES_CERTS_CLIENT_KEY_PASSPHRASE.propertyName, EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_PASSPHRASE);
+        properties.put(KUBERNETES_AUTH_BASIC_USERNAME.propertyName, EXPECTED_KUBERNETES_AUTH_BASIC_USERNAME);
+        properties.put(KUBERNETES_AUTH_BASIC_PASSWORD.propertyName, EXPECTED_KUBERNETES_AUTH_BASIC_PASSWORD);
+        properties.put(KUBERNETES_AUTH_TOKEN.propertyName, EXPECTED_KUBERNETES_OAUTH_TOKEN);
+        properties.put(KUBERNETES_WATCH_RECONNECTINTERVAL.propertyName, EXPECTED_KUBERNETES_WATCH_RECONNECT_INTERVAL);
+        properties.put(KUBERNETES_WATCH_RECONNECTLIMIT.propertyName, EXPECTED_KUBERNETES_WATCH_RECONNECT_LIMIT);
+        properties.put(KUBERNETES_USER_AGENT.propertyName, EXPECTED_KUBERNETES_USER_AGENT);
+        properties.put(KUBERNETES_TLS_VERSIONS.propertyName, EXPECTED_KUBERNETES_TLS_VERSION);
+        properties.put(KUBERNETES_TRUSTSTORE_FILE.propertyName, EXPECTED_KUBERNETES_TRUSTSTORE_FILE);
+        properties.put(KUBERNETES_TRUSTSTORE_PASSPHRASE.propertyName, EXPECTED_KUBERNETES_TRUSTSTORE_PASSPHRASE);
+        properties.put(KUBERNETES_KEYSTORE_FILE.propertyName, EXPECTED_KUBERNETES_KEYSTORE_FILE);
+        properties.put(KUBERNETES_KEYSTORE_PASSPHRASE.propertyName, EXPECTED_KUBERNETES_KEYSTORE_PASSPHRASE);
+        update();
+        assertEquals(EXPECTED_KUBERNETES_API_VERSION, registeredService.getKubernetesApiVersion());
+        assertEquals(Boolean.parseBoolean(EXPECTED_KUBERNETES_TRUST_CERTIFICATES), registeredService.isKubernetesTrustCertificates());
+        assertEquals(Boolean.parseBoolean(EXPECTED_KUBERNETES_DISABLE_HOSTNAME_VERIFICATION), registeredService.isKubernetesDisableHostnameVerification());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CA_FILE, registeredService.getKubernetesCertsCaFile());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CA_DATA, registeredService.getKubernetesCertsCaData());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CLIENT_FILE, registeredService.getKubernetesCertsClientFile());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CLIENT_DATA, registeredService.getKubernetesCertsClientData());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_FILE, registeredService.getKubernetesCertsClientKeyFile());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_DATA, registeredService.getKubernetesCertsClientKeyData());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_ALGO, registeredService.getKubernetesCertsClientKeyAlgo());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_PASSPHRASE, registeredService.getKubernetesCertsClientKeyPassphrase());
+        assertEquals(EXPECTED_KUBERNETES_AUTH_BASIC_USERNAME, registeredService.getKubernetesAuthBasicUsername());
+        assertEquals(EXPECTED_KUBERNETES_AUTH_BASIC_PASSWORD, registeredService.getKubernetesAuthBasicPassword());
+        assertEquals(EXPECTED_KUBERNETES_OAUTH_TOKEN, registeredService.getKubernetesOauthToken());
+        assertEquals(Integer.parseInt(EXPECTED_KUBERNETES_WATCH_RECONNECT_INTERVAL), registeredService.getKubernetesWatchReconnectInterval());
+        assertEquals(Integer.parseInt(EXPECTED_KUBERNETES_WATCH_RECONNECT_LIMIT), registeredService.getKubernetesWatchReconnectLimit());
+        assertEquals(EXPECTED_KUBERNETES_USER_AGENT, registeredService.getKubernetesUserAgent());
+        assertEquals(EXPECTED_KUBERNETES_TLS_VERSION, registeredService.getKubernetesTlsVersion());
+        assertEquals(EXPECTED_KUBERNETES_TRUSTSTORE_FILE, registeredService.getKubernetesTruststoreFile());
+        assertEquals(EXPECTED_KUBERNETES_TRUSTSTORE_PASSPHRASE, registeredService.getKubernetesTruststorePassphrase());
+        assertEquals(EXPECTED_KUBERNETES_KEYSTORE_FILE, registeredService.getKubernetesKeystoreFile());
+        assertEquals(EXPECTED_KUBERNETES_KEYSTORE_PASSPHRASE, registeredService.getKubernetesKeystorePassphrase());
+    }
+}
diff --git a/kubernetes/src/test/java/org/apache/karaf/cellar/kubernetes/KubernetesDiscoveryServiceTest.java b/kubernetes/src/test/java/org/apache/karaf/cellar/kubernetes/KubernetesDiscoveryServiceTest.java
new file mode 100644
index 0000000..e6e792e
--- /dev/null
+++ b/kubernetes/src/test/java/org/apache/karaf/cellar/kubernetes/KubernetesDiscoveryServiceTest.java
@@ -0,0 +1,167 @@
+package org.apache.karaf.cellar.kubernetes;
+
+import io.fabric8.kubernetes.api.model.DoneablePod;
+import io.fabric8.kubernetes.api.model.ObjectMeta;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.PodList;
+import io.fabric8.kubernetes.api.model.PodStatus;
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.dsl.MixedOperation;
+import io.fabric8.kubernetes.client.dsl.PodResource;
+import okhttp3.TlsVersion;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.mock;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class KubernetesDiscoveryServiceTest {
+    static final String EXPECTED_KUBERNETES_MASTER = "http://master/";
+    static final String EXPECTED_KUBERNETES_API_VERSION = "api version";
+    static final String EXPECTED_KUBERNETES_TRUST_CERTIFICATES = "true";
+    static final String EXPECTED_KUBERNETES_DISABLE_HOSTNAME_VERIFICATION = "true";
+    static final String EXPECTED_KUBERNETES_CERTS_CA_FILE = "certs ca file";
+    static final String EXPECTED_KUBERNETES_CERTS_CA_DATA = "certs ca data";
+    static final String EXPECTED_KUBERNETES_CERTS_CLIENT_FILE = "certs client file";
+    static final String EXPECTED_KUBERNETES_CERTS_CLIENT_DATA = "certs client data";
+    static final String EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_FILE = "certs client key file";
+    static final String EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_DATA = "certs client key data";
+    static final String EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_ALGO = "certs client key algo";
+    static final String EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_PASSPHRASE = "certs client key passphrase";
+    static final String EXPECTED_KUBERNETES_AUTH_BASIC_USERNAME = "auth basic username";
+    static final String EXPECTED_KUBERNETES_AUTH_BASIC_PASSWORD = "auth basic password";
+    static final String EXPECTED_KUBERNETES_OAUTH_TOKEN = "oauth token";
+    static final String EXPECTED_KUBERNETES_WATCH_RECONNECT_INTERVAL = "10";
+    static final String EXPECTED_KUBERNETES_WATCH_RECONNECT_LIMIT = "20";
+    static final String EXPECTED_KUBERNETES_USER_AGENT = "user agent";
+    static final String EXPECTED_KUBERNETES_TLS_VERSION = "TLSv1.3";
+    static final String EXPECTED_KUBERNETES_TRUSTSTORE_FILE = "truststore file";
+    static final String EXPECTED_KUBERNETES_TRUSTSTORE_PASSPHRASE = "truststore passphrase";
+    static final String EXPECTED_KUBERNETES_KEYSTORE_FILE = "keystore file";
+    static final String EXPECTED_KUBERNETES_KEYSTORE_PASSPHRASE = "keystore passphrase";
+    static final String EXPECTED_KUBERNETES_POD_LABEL_KEY = "pod label key";
+    static final String EXPECTED_KUBERNETES_POD_LABEL_VALUE = "pod label value";
+    static final String EXPECTED_POD_ID = "192.168.0.1";
+    private KubernetesClient kubernetesClient = mock(KubernetesClient.class);
+    private final MixedOperation<Pod, PodList, DoneablePod, PodResource<Pod, DoneablePod>> pods = mock(MixedOperation.class);
+    private final PodList podList = new PodList();
+    private final KubernetesDiscoveryService service = new KubernetesDiscoveryService();
+    private final Pod pod = new Pod();
+    private final ObjectMeta metadata = new ObjectMeta();
+    private final PodStatus status = new PodStatus();
+    private final List<Pod> items = Arrays.asList(pod);
+    private final Map<String, String> labels = new HashMap<>();
+
+    @Before
+    public void setup() {
+        labels.put(EXPECTED_KUBERNETES_POD_LABEL_KEY, EXPECTED_KUBERNETES_POD_LABEL_VALUE);
+        service.setKubernetesPodLabelKey(EXPECTED_KUBERNETES_POD_LABEL_KEY);
+        service.setKubernetesPodLabelValue(EXPECTED_KUBERNETES_POD_LABEL_VALUE);
+        service.setKubernetesClient(kubernetesClient);
+
+        expect(kubernetesClient.pods()).andReturn(pods);
+        expect(pods.list()).andReturn(podList);
+        podList.setItems(items);
+        pod.setMetadata(metadata);
+        pod.setStatus(status);
+        metadata.setLabels(labels);
+        status.setPodIP(EXPECTED_POD_ID);
+        replay(kubernetesClient, pods);
+    }
+
+    @Test
+    public void createConfig() {
+        service.setKubernetesMaster(EXPECTED_KUBERNETES_MASTER);
+        service.setKubernetesApiVersion(EXPECTED_KUBERNETES_API_VERSION);
+        service.setKubernetesTrustCertificates(EXPECTED_KUBERNETES_TRUST_CERTIFICATES);
+        service.setKubernetesDisableHostnameVerification(EXPECTED_KUBERNETES_DISABLE_HOSTNAME_VERIFICATION);
+        service.setKubernetesCertsCaFile(EXPECTED_KUBERNETES_CERTS_CA_FILE);
+        service.setKubernetesCertsCaData(EXPECTED_KUBERNETES_CERTS_CA_DATA);
+        service.setKubernetesCertsClientFile(EXPECTED_KUBERNETES_CERTS_CLIENT_FILE);
+        service.setKubernetesCertsClientData(EXPECTED_KUBERNETES_CERTS_CLIENT_DATA);
+        service.setKubernetesCertsClientKeyFile(EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_FILE);
+        service.setKubernetesCertsClientKeyData(EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_DATA);
+        service.setKubernetesCertsClientKeyAlgo(EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_ALGO);
+        service.setKubernetesCertsClientKeyPassphrase(EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_PASSPHRASE);
+        service.setKubernetesAuthBasicUsername(EXPECTED_KUBERNETES_AUTH_BASIC_USERNAME);
+        service.setKubernetesAuthBasicPassword(EXPECTED_KUBERNETES_AUTH_BASIC_PASSWORD);
+        service.setKubernetesOauthToken(EXPECTED_KUBERNETES_OAUTH_TOKEN);
+        service.setKubernetesWatchReconnectInterval(EXPECTED_KUBERNETES_WATCH_RECONNECT_INTERVAL);
+        service.setKubernetesWatchReconnectLimit(EXPECTED_KUBERNETES_WATCH_RECONNECT_LIMIT);
+        service.setKubernetesUserAgent(EXPECTED_KUBERNETES_USER_AGENT);
+        service.setKubernetesTlsVersion(EXPECTED_KUBERNETES_TLS_VERSION);
+        service.setKubernetesTruststoreFile(EXPECTED_KUBERNETES_TRUSTSTORE_FILE);
+        service.setKubernetesTruststorePassphrase(EXPECTED_KUBERNETES_TRUSTSTORE_PASSPHRASE);
+        service.setKubernetesKeystoreFile(EXPECTED_KUBERNETES_KEYSTORE_FILE);
+        service.setKubernetesKeystorePassphrase(EXPECTED_KUBERNETES_KEYSTORE_PASSPHRASE);
+
+        Config config = service.createConfig();
+        assertEquals(EXPECTED_KUBERNETES_MASTER, config.getMasterUrl());
+        assertEquals(EXPECTED_KUBERNETES_API_VERSION, config.getApiVersion());
+        assertTrue(config.isTrustCerts());
+        assertTrue(config.isDisableHostnameVerification());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CA_FILE, config.getCaCertFile());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CA_DATA, config.getCaCertData());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CLIENT_FILE, config.getClientCertFile());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CLIENT_DATA, config.getClientCertData());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_FILE, config.getClientKeyFile());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_DATA, config.getClientKeyData());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_ALGO, config.getClientKeyAlgo());
+        assertEquals(EXPECTED_KUBERNETES_CERTS_CLIENT_KEY_PASSPHRASE, config.getClientKeyPassphrase());
+        assertEquals(EXPECTED_KUBERNETES_AUTH_BASIC_USERNAME, config.getUsername());
+        assertEquals(EXPECTED_KUBERNETES_AUTH_BASIC_PASSWORD, config.getPassword());
+        assertEquals(EXPECTED_KUBERNETES_OAUTH_TOKEN, config.getOauthToken());
+        assertEquals(Integer.parseInt(EXPECTED_KUBERNETES_WATCH_RECONNECT_INTERVAL), config.getWatchReconnectInterval());
+        assertEquals(Integer.parseInt(EXPECTED_KUBERNETES_WATCH_RECONNECT_LIMIT), config.getWatchReconnectLimit());
+        assertEquals(EXPECTED_KUBERNETES_USER_AGENT, config.getUserAgent());
+
+        TlsVersion[] tlsVersions = config.getTlsVersions();
+        assertEquals(1, tlsVersions.length);
+        assertEquals(TlsVersion.TLS_1_3, tlsVersions[0]);
+
+        assertEquals(EXPECTED_KUBERNETES_TRUSTSTORE_FILE, config.getTrustStoreFile());
+        assertEquals(EXPECTED_KUBERNETES_TRUSTSTORE_PASSPHRASE, config.getTrustStorePassphrase());
+        assertEquals(EXPECTED_KUBERNETES_KEYSTORE_FILE, config.getKeyStoreFile());
+        assertEquals(EXPECTED_KUBERNETES_KEYSTORE_PASSPHRASE, config.getKeyStorePassphrase());
+    }
+
+    @Test
+    public void discoverMembers() {
+        Set<String> memberIps = service.discoverMembers();
+        assertEquals(1, memberIps.size());
+        assertEquals(EXPECTED_POD_ID, memberIps.iterator().next());
+    }
+
+    @Test
+    public void discoverMembersUnexpectedPodLabelKey() {
+        service.setKubernetesPodLabelKey("unexpected");
+        assertTrue(service.discoverMembers().isEmpty());
+    }
+
+    @Test
+    public void discoverMembersUnexpectedPodLabelValue() {
+        service.setKubernetesPodLabelValue("unexpected");
+        assertTrue(service.discoverMembers().isEmpty());
+    }
+
+    @Test
+    public void discoverMembersLogException() {
+        reset(kubernetesClient);
+        expect(kubernetesClient.pods()).andThrow(new RuntimeException("Test exception"));
+        replay(kubernetesClient);
+
+        // Should return empty set because exception was caught and logged
+        assertTrue(service.discoverMembers().isEmpty());
+    }
+}